Lots of various golint fixes

Changes some names to match go conventions
Comments all exported methods
Removes dot imports
This commit is contained in:
Brian Bland 2014-11-17 15:44:07 -08:00
parent b5cf681458
commit 88795e0a14
19 changed files with 417 additions and 257 deletions

View file

@ -79,7 +79,7 @@ type clientImpl struct {
// TODO(bbland): use consistent route generation between server and client // TODO(bbland): use consistent route generation between server and client
func (r *clientImpl) GetImageManifest(name, tag string) (*registry.ImageManifest, error) { func (r *clientImpl) GetImageManifest(name, tag string) (*registry.ImageManifest, error) {
response, err := http.Get(r.imageManifestUrl(name, tag)) response, err := http.Get(r.imageManifestURL(name, tag))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -100,7 +100,7 @@ func (r *clientImpl) GetImageManifest(name, tag string) (*registry.ImageManifest
} }
return nil, errors return nil, errors
default: default:
return nil, &registry.UnexpectedHttpStatusError{Status: response.Status} return nil, &registry.UnexpectedHTTPStatusError{Status: response.Status}
} }
decoder := json.NewDecoder(response.Body) decoder := json.NewDecoder(response.Body)
@ -120,7 +120,7 @@ func (r *clientImpl) PutImageManifest(name, tag string, manifest *registry.Image
} }
putRequest, err := http.NewRequest("PUT", putRequest, err := http.NewRequest("PUT",
r.imageManifestUrl(name, tag), bytes.NewReader(manifestBytes)) r.imageManifestURL(name, tag), bytes.NewReader(manifestBytes))
if err != nil { if err != nil {
return err return err
} }
@ -144,13 +144,13 @@ func (r *clientImpl) PutImageManifest(name, tag string, manifest *registry.Image
} }
return errors return errors
default: default:
return &registry.UnexpectedHttpStatusError{Status: response.Status} return &registry.UnexpectedHTTPStatusError{Status: response.Status}
} }
} }
func (r *clientImpl) DeleteImage(name, tag string) error { func (r *clientImpl) DeleteImage(name, tag string) error {
deleteRequest, err := http.NewRequest("DELETE", deleteRequest, err := http.NewRequest("DELETE",
r.imageManifestUrl(name, tag), nil) r.imageManifestURL(name, tag), nil)
if err != nil { if err != nil {
return err return err
} }
@ -176,7 +176,7 @@ func (r *clientImpl) DeleteImage(name, tag string) error {
} }
return errors return errors
default: default:
return &registry.UnexpectedHttpStatusError{Status: response.Status} return &registry.UnexpectedHTTPStatusError{Status: response.Status}
} }
return nil return nil
@ -204,7 +204,7 @@ func (r *clientImpl) ListImageTags(name string) ([]string, error) {
} }
return nil, errors return nil, errors
default: default:
return nil, &registry.UnexpectedHttpStatusError{Status: response.Status} return nil, &registry.UnexpectedHTTPStatusError{Status: response.Status}
} }
tags := struct { tags := struct {
@ -258,7 +258,7 @@ func (r *clientImpl) GetImageLayer(name, tarsum string, byteOffset int) (io.Read
return nil, 0, errors return nil, 0, errors
default: default:
response.Body.Close() response.Body.Close()
return nil, 0, &registry.UnexpectedHttpStatusError{Status: response.Status} return nil, 0, &registry.UnexpectedHTTPStatusError{Status: response.Status}
} }
} }
@ -290,7 +290,7 @@ func (r *clientImpl) InitiateLayerUpload(name, tarsum string) (string, error) {
} }
return "", errors return "", errors
default: default:
return "", &registry.UnexpectedHttpStatusError{Status: response.Status} return "", &registry.UnexpectedHTTPStatusError{Status: response.Status}
} }
} }
@ -316,7 +316,7 @@ func (r *clientImpl) GetLayerUploadStatus(location string) (int, int, error) {
} }
return 0, 0, errors return 0, 0, errors
default: default:
return 0, 0, &registry.UnexpectedHttpStatusError{Status: response.Status} return 0, 0, &registry.UnexpectedHTTPStatusError{Status: response.Status}
} }
} }
@ -358,7 +358,7 @@ func (r *clientImpl) UploadLayer(location string, layer io.ReadCloser, length in
} }
return errors return errors
default: default:
return &registry.UnexpectedHttpStatusError{Status: response.Status} return &registry.UnexpectedHTTPStatusError{Status: response.Status}
} }
} }
@ -409,7 +409,7 @@ func (r *clientImpl) UploadLayerChunk(location string, layerChunk io.ReadCloser,
} }
return errors return errors
default: default:
return &registry.UnexpectedHttpStatusError{Status: response.Status} return &registry.UnexpectedHTTPStatusError{Status: response.Status}
} }
} }
@ -451,7 +451,7 @@ func (r *clientImpl) FinishChunkedLayerUpload(location string, length int, check
} }
return errors return errors
default: default:
return &registry.UnexpectedHttpStatusError{Status: response.Status} return &registry.UnexpectedHTTPStatusError{Status: response.Status}
} }
} }
@ -483,13 +483,13 @@ func (r *clientImpl) CancelLayerUpload(location string) error {
} }
return errors return errors
default: default:
return &registry.UnexpectedHttpStatusError{Status: response.Status} return &registry.UnexpectedHTTPStatusError{Status: response.Status}
} }
} }
// imageManifestUrl is a helper method for returning the full url to an image // imageManifestURL is a helper method for returning the full url to an image
// manifest // manifest
func (r *clientImpl) imageManifestUrl(name, tag string) string { func (r *clientImpl) imageManifestURL(name, tag string) string {
return fmt.Sprintf("%s/v2/%s/image/%s", r.Endpoint, name, tag) return fmt.Sprintf("%s/v2/%s/image/%s", r.Endpoint, name, tag)
} }

View file

@ -7,11 +7,24 @@ import (
) )
const ( const (
// RepositoryNameComponentMinLength is the minimum number of characters in a
// single repository name slash-delimited component
RepositoryNameComponentMinLength = 2 RepositoryNameComponentMinLength = 2
// RepositoryNameComponentMaxLength is the maximum number of characters in a
// single repository name slash-delimited component
RepositoryNameComponentMaxLength = 30 RepositoryNameComponentMaxLength = 30
RepositoryNameMinComponents = 2 // RepositoryNameMinComponents is the minimum number of slash-delimited
RepositoryNameMaxComponents = 5 // components that a repository name must have
RepositoryNameMinComponents = 2
// RepositoryNameMaxComponents is the maximum number of slash-delimited
// components that a repository name must have
RepositoryNameMaxComponents = 5
// RepositoryNameTotalLengthMax is the maximum total number of characters in
// a repository name
RepositoryNameTotalLengthMax = 255 RepositoryNameTotalLengthMax = 255
) )
@ -19,6 +32,9 @@ const (
// start with at least two letters or numbers, with following parts able to // start with at least two letters or numbers, with following parts able to
// separated by one period, dash or underscore. // separated by one period, dash or underscore.
var RepositoryNameComponentRegexp = regexp.MustCompile(`[a-z0-9]+(?:[._-][a-z0-9]+)*`) var RepositoryNameComponentRegexp = regexp.MustCompile(`[a-z0-9]+(?:[._-][a-z0-9]+)*`)
// RepositoryNameComponentAnchoredRegexp is the version of
// RepositoryNameComponentRegexp which must completely match the content
var RepositoryNameComponentAnchoredRegexp = regexp.MustCompile(`^` + RepositoryNameComponentRegexp.String() + `$`) var RepositoryNameComponentAnchoredRegexp = regexp.MustCompile(`^` + RepositoryNameComponentRegexp.String() + `$`)
// TODO(stevvooe): RepositoryName needs to be limited to some fixed length. // TODO(stevvooe): RepositoryName needs to be limited to some fixed length.
@ -35,13 +51,30 @@ var TagNameRegexp = regexp.MustCompile(`[\w][\w.-]{0,127}`)
// TODO(stevvooe): Contribute these exports back to core, so they are shared. // TODO(stevvooe): Contribute these exports back to core, so they are shared.
var ( var (
// ErrRepositoryNameComponentShort is returned when a repository name
// contains a component which is shorter than
// RepositoryNameComponentMinLength
ErrRepositoryNameComponentShort = fmt.Errorf("respository name component must be %v or more characters", RepositoryNameComponentMinLength) ErrRepositoryNameComponentShort = fmt.Errorf("respository name component must be %v or more characters", RepositoryNameComponentMinLength)
ErrRepositoryNameComponentLong = fmt.Errorf("respository name component must be %v characters or less", RepositoryNameComponentMaxLength)
// ErrRepositoryNameComponentLong is returned when a repository name
// contains a component which is longer than
// RepositoryNameComponentMaxLength
ErrRepositoryNameComponentLong = fmt.Errorf("respository name component must be %v characters or less", RepositoryNameComponentMaxLength)
// ErrRepositoryNameMissingComponents is returned when a repository name
// contains fewer than RepositoryNameMinComponents components
ErrRepositoryNameMissingComponents = fmt.Errorf("repository name must have at least %v components", RepositoryNameMinComponents) ErrRepositoryNameMissingComponents = fmt.Errorf("repository name must have at least %v components", RepositoryNameMinComponents)
// ErrRepositoryNameTooManyComponents is returned when a repository name
// contains more than RepositoryNameMaxComponents components
ErrRepositoryNameTooManyComponents = fmt.Errorf("repository name %v or less components", RepositoryNameMaxComponents) ErrRepositoryNameTooManyComponents = fmt.Errorf("repository name %v or less components", RepositoryNameMaxComponents)
ErrRepositoryNameLong = fmt.Errorf("repository name must not be more than %v characters", RepositoryNameTotalLengthMax) // ErrRepositoryNameLong is returned when a repository name is longer than
// RepositoryNameTotalLengthMax
ErrRepositoryNameLong = fmt.Errorf("repository name must not be more than %v characters", RepositoryNameTotalLengthMax)
// ErrRepositoryNameComponentInvalid is returned when a repository name does
// not match RepositoryNameComponentRegexp
ErrRepositoryNameComponentInvalid = fmt.Errorf("repository name component must match %q", RepositoryNameComponentRegexp.String()) ErrRepositoryNameComponentInvalid = fmt.Errorf("repository name component must match %q", RepositoryNameComponentRegexp.String())
) )

View file

@ -25,6 +25,8 @@ type TarSumInfo struct {
Digest string Digest string
} }
// InvalidTarSumError provides informations about a TarSum that cannot be parsed
// by ParseTarSum.
type InvalidTarSumError struct { type InvalidTarSumError struct {
TarSum string TarSum string
} }

View file

@ -23,9 +23,9 @@ type Configuration struct {
Storage Storage `yaml:"storage"` Storage Storage `yaml:"storage"`
} }
// v_0_1_Configuration is a Version 0.1 Configuration struct // v0_1Configuration is a Version 0.1 Configuration struct
// This is currently aliased to Configuration, as it is the current version // This is currently aliased to Configuration, as it is the current version
type v_0_1_Configuration Configuration type v0_1Configuration Configuration
// Version is a major/minor version pair of the form Major.Minor // Version is a major/minor version pair of the form Major.Minor
// Major version upgrades indicate structure or type changes // Major version upgrades indicate structure or type changes
@ -195,7 +195,7 @@ func Parse(in []byte) (*Configuration, error) {
// Parse the remainder of the configuration depending on the provided version // Parse the remainder of the configuration depending on the provided version
switch untypedConfig.Version { switch untypedConfig.Version {
case "0.1": case "0.1":
config, err = parseV_0_1_Registry(in) config, err = parseV0_1Registry(in)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -206,11 +206,11 @@ func Parse(in []byte) (*Configuration, error) {
return config, nil return config, nil
} }
// parseV_0_1_Registry parses a registry Configuration for Version 0.1 // parseV0_1Registry parses a registry Configuration for Version 0.1
func parseV_0_1_Registry(in []byte) (*Configuration, error) { func parseV0_1Registry(in []byte) (*Configuration, error) {
envMap := getEnvMap() envMap := getEnvMap()
var config v_0_1_Configuration var config v0_1Configuration
err := yaml.Unmarshal(in, &config) err := yaml.Unmarshal(in, &config)
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -12,7 +12,7 @@ import (
// Hook up gocheck into the "go test" runner // Hook up gocheck into the "go test" runner
func Test(t *testing.T) { TestingT(t) } func Test(t *testing.T) { TestingT(t) }
// configStruct is a canonical example configuration, which should map to configYamlV_0_1 // configStruct is a canonical example configuration, which should map to configYamlV0_1
var configStruct = Configuration{ var configStruct = Configuration{
Version: "0.1", Version: "0.1",
Loglevel: "info", Loglevel: "info",
@ -31,8 +31,8 @@ var configStruct = Configuration{
}, },
} }
// configYamlV_0_1 is a Version 0.1 yaml document representing configStruct // configYamlV0_1 is a Version 0.1 yaml document representing configStruct
var configYamlV_0_1 = ` var configYamlV0_1 = `
version: 0.1 version: 0.1
loglevel: info loglevel: info
storage: storage:
@ -48,9 +48,9 @@ storage:
port: ~ port: ~
` `
// inmemoryConfigYamlV_0_1 is a Version 0.1 yaml document specifying an inmemory storage driver with // inmemoryConfigYamlV0_1 is a Version 0.1 yaml document specifying an inmemory storage driver with
// no parameters // no parameters
var inmemoryConfigYamlV_0_1 = ` var inmemoryConfigYamlV0_1 = `
version: 0.1 version: 0.1
loglevel: info loglevel: info
storage: inmemory storage: inmemory
@ -77,9 +77,9 @@ func (suite *ConfigSuite) TestMarshalRoundtrip(c *C) {
c.Assert(config, DeepEquals, suite.expectedConfig) c.Assert(config, DeepEquals, suite.expectedConfig)
} }
// TestParseSimple validates that configYamlV_0_1 can be parsed into a struct matching configStruct // TestParseSimple validates that configYamlV0_1 can be parsed into a struct matching configStruct
func (suite *ConfigSuite) TestParseSimple(c *C) { func (suite *ConfigSuite) TestParseSimple(c *C) {
config, err := Parse([]byte(configYamlV_0_1)) config, err := Parse([]byte(configYamlV0_1))
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(config, DeepEquals, suite.expectedConfig) c.Assert(config, DeepEquals, suite.expectedConfig)
} }
@ -89,7 +89,7 @@ func (suite *ConfigSuite) TestParseSimple(c *C) {
func (suite *ConfigSuite) TestParseInmemory(c *C) { func (suite *ConfigSuite) TestParseInmemory(c *C) {
suite.expectedConfig.Storage = Storage{"inmemory": Parameters{}} suite.expectedConfig.Storage = Storage{"inmemory": Parameters{}}
config, err := Parse([]byte(inmemoryConfigYamlV_0_1)) config, err := Parse([]byte(inmemoryConfigYamlV0_1))
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(config, DeepEquals, suite.expectedConfig) c.Assert(config, DeepEquals, suite.expectedConfig)
} }
@ -100,7 +100,7 @@ func (suite *ConfigSuite) TestParseWithSameEnvStorage(c *C) {
os.Setenv("REGISTRY_STORAGE", "s3") os.Setenv("REGISTRY_STORAGE", "s3")
os.Setenv("REGISTRY_STORAGE_S3_REGION", "us-east-1") os.Setenv("REGISTRY_STORAGE_S3_REGION", "us-east-1")
config, err := Parse([]byte(configYamlV_0_1)) config, err := Parse([]byte(configYamlV0_1))
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(config, DeepEquals, suite.expectedConfig) c.Assert(config, DeepEquals, suite.expectedConfig)
} }
@ -117,7 +117,7 @@ func (suite *ConfigSuite) TestParseWithDifferentEnvStorageParams(c *C) {
os.Setenv("REGISTRY_STORAGE_S3_SECURE", "true") os.Setenv("REGISTRY_STORAGE_S3_SECURE", "true")
os.Setenv("REGISTRY_STORAGE_S3_NEWPARAM", "some Value") os.Setenv("REGISTRY_STORAGE_S3_NEWPARAM", "some Value")
config, err := Parse([]byte(configYamlV_0_1)) config, err := Parse([]byte(configYamlV0_1))
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(config, DeepEquals, suite.expectedConfig) c.Assert(config, DeepEquals, suite.expectedConfig)
} }
@ -129,7 +129,7 @@ func (suite *ConfigSuite) TestParseWithDifferentEnvStorageType(c *C) {
os.Setenv("REGISTRY_STORAGE", "inmemory") os.Setenv("REGISTRY_STORAGE", "inmemory")
config, err := Parse([]byte(configYamlV_0_1)) config, err := Parse([]byte(configYamlV0_1))
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(config, DeepEquals, suite.expectedConfig) c.Assert(config, DeepEquals, suite.expectedConfig)
} }
@ -144,7 +144,7 @@ func (suite *ConfigSuite) TestParseWithDifferentEnvStorageTypeAndParams(c *C) {
os.Setenv("REGISTRY_STORAGE", "filesystem") os.Setenv("REGISTRY_STORAGE", "filesystem")
os.Setenv("REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY", "/tmp/testroot") os.Setenv("REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY", "/tmp/testroot")
config, err := Parse([]byte(configYamlV_0_1)) config, err := Parse([]byte(configYamlV0_1))
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(config, DeepEquals, suite.expectedConfig) c.Assert(config, DeepEquals, suite.expectedConfig)
} }
@ -154,7 +154,7 @@ func (suite *ConfigSuite) TestParseWithDifferentEnvStorageTypeAndParams(c *C) {
func (suite *ConfigSuite) TestParseWithSameEnvLoglevel(c *C) { func (suite *ConfigSuite) TestParseWithSameEnvLoglevel(c *C) {
os.Setenv("REGISTRY_LOGLEVEL", "info") os.Setenv("REGISTRY_LOGLEVEL", "info")
config, err := Parse([]byte(configYamlV_0_1)) config, err := Parse([]byte(configYamlV0_1))
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(config, DeepEquals, suite.expectedConfig) c.Assert(config, DeepEquals, suite.expectedConfig)
} }
@ -166,7 +166,7 @@ func (suite *ConfigSuite) TestParseWithDifferentEnvLoglevel(c *C) {
os.Setenv("REGISTRY_LOGLEVEL", "error") os.Setenv("REGISTRY_LOGLEVEL", "error")
config, err := Parse([]byte(configYamlV_0_1)) config, err := Parse([]byte(configYamlV0_1))
c.Assert(err, IsNil) c.Assert(err, IsNil)
c.Assert(config, DeepEquals, suite.expectedConfig) c.Assert(config, DeepEquals, suite.expectedConfig)
} }

View file

@ -10,18 +10,43 @@ import (
type ErrorCode int type ErrorCode int
const ( const (
// ErrorCodeUnknown is a catch-all for errors not defined below.
ErrorCodeUnknown ErrorCode = iota ErrorCodeUnknown ErrorCode = iota
// The following errors can happen during a layer upload. // The following errors can happen during a layer upload.
// ErrorCodeInvalidChecksum is returned when uploading a layer if the
// provided checksum does not match the layer contents.
ErrorCodeInvalidChecksum ErrorCodeInvalidChecksum
// ErrorCodeInvalidLength is returned when uploading a layer if the provided
// length does not match the content length.
ErrorCodeInvalidLength ErrorCodeInvalidLength
// ErrorCodeInvalidTarsum is returned when the provided tarsum does not
// match the computed tarsum of the contents.
ErrorCodeInvalidTarsum ErrorCodeInvalidTarsum
// The following errors can happen during manifest upload. // The following errors can happen during manifest upload.
// ErrorCodeInvalidName is returned when the name in the manifest does not
// match the provided name.
ErrorCodeInvalidName ErrorCodeInvalidName
// ErrorCodeInvalidTag is returned when the tag in the manifest does not
// match the provided tag.
ErrorCodeInvalidTag ErrorCodeInvalidTag
// ErrorCodeUnverifiedManifest is returned when the manifest fails signature
// validation.
ErrorCodeUnverifiedManifest ErrorCodeUnverifiedManifest
// ErrorCodeUnknownLayer is returned when the manifest references a
// nonexistent layer.
ErrorCodeUnknownLayer ErrorCodeUnknownLayer
// ErrorCodeUntrustedSignature is returned when the manifest is signed by an
// untrusted source.
ErrorCodeUntrustedSignature ErrorCodeUntrustedSignature
) )
@ -83,6 +108,7 @@ func (ec ErrorCode) String() string {
return s return s
} }
// Message returned the human-readable error message for this error code.
func (ec ErrorCode) Message() string { func (ec ErrorCode) Message() string {
m, ok := errorCodesMessages[ec] m, ok := errorCodesMessages[ec]
@ -93,16 +119,20 @@ func (ec ErrorCode) Message() string {
return m return m
} }
// MarshalText encodes the receiver into UTF-8-encoded text and returns the
// result.
func (ec ErrorCode) MarshalText() (text []byte, err error) { func (ec ErrorCode) MarshalText() (text []byte, err error) {
return []byte(ec.String()), nil return []byte(ec.String()), nil
} }
// UnmarshalText decodes the form generated by MarshalText.
func (ec *ErrorCode) UnmarshalText(text []byte) error { func (ec *ErrorCode) UnmarshalText(text []byte) error {
*ec = stringToErrorCode[string(text)] *ec = stringToErrorCode[string(text)]
return nil return nil
} }
// Error provides a wrapper around ErrorCode with extra Details provided.
type Error struct { type Error struct {
Code ErrorCode `json:"code,omitempty"` Code ErrorCode `json:"code,omitempty"`
Message string `json:"message,omitempty"` Message string `json:"message,omitempty"`
@ -173,7 +203,7 @@ type DetailUnknownLayer struct {
} }
// RepositoryNotFoundError is returned when making an operation against a // RepositoryNotFoundError is returned when making an operation against a
// repository that does not exist in the registry // repository that does not exist in the registry.
type RepositoryNotFoundError struct { type RepositoryNotFoundError struct {
Name string Name string
} }
@ -183,7 +213,7 @@ func (e *RepositoryNotFoundError) Error() string {
} }
// ImageManifestNotFoundError is returned when making an operation against a // ImageManifestNotFoundError is returned when making an operation against a
// given image manifest that does not exist in the registry // given image manifest that does not exist in the registry.
type ImageManifestNotFoundError struct { type ImageManifestNotFoundError struct {
Name string Name string
Tag string Tag string
@ -195,7 +225,7 @@ func (e *ImageManifestNotFoundError) Error() string {
} }
// LayerAlreadyExistsError is returned when attempting to create a new layer // LayerAlreadyExistsError is returned when attempting to create a new layer
// that already exists in the registry // that already exists in the registry.
type LayerAlreadyExistsError struct { type LayerAlreadyExistsError struct {
Name string Name string
TarSum string TarSum string
@ -207,7 +237,7 @@ func (e *LayerAlreadyExistsError) Error() string {
} }
// LayerNotFoundError is returned when making an operation against a given image // LayerNotFoundError is returned when making an operation against a given image
// layer that does not exist in the registry // layer that does not exist in the registry.
type LayerNotFoundError struct { type LayerNotFoundError struct {
Name string Name string
TarSum string TarSum string
@ -221,7 +251,7 @@ func (e *LayerNotFoundError) Error() string {
// LayerUploadNotFoundError is returned when making a layer upload operation // LayerUploadNotFoundError is returned when making a layer upload operation
// against an invalid layer upload location url // against an invalid layer upload location url
// This may be the result of using a cancelled, completed, or stale upload // This may be the result of using a cancelled, completed, or stale upload
// locationn // location.
type LayerUploadNotFoundError struct { type LayerUploadNotFoundError struct {
Location string Location string
} }
@ -232,9 +262,9 @@ func (e *LayerUploadNotFoundError) Error() string {
} }
// LayerUploadInvalidRangeError is returned when attempting to upload an image // LayerUploadInvalidRangeError is returned when attempting to upload an image
// layer chunk that is out of order // layer chunk that is out of order.
// This provides the known LayerSize and LastValidRange which can be used to // This provides the known LayerSize and LastValidRange which can be used to
// resume the upload // resume the upload.
type LayerUploadInvalidRangeError struct { type LayerUploadInvalidRangeError struct {
Location string Location string
LastValidRange int LastValidRange int
@ -247,12 +277,12 @@ func (e *LayerUploadInvalidRangeError) Error() string {
e.Location, e.LastValidRange, e.LayerSize) e.Location, e.LastValidRange, e.LayerSize)
} }
// UnexpectedHttpStatusError is returned when an unexpected http status is // UnexpectedHTTPStatusError is returned when an unexpected HTTP status is
// returned when making a registry api call // returned when making a registry api call.
type UnexpectedHttpStatusError struct { type UnexpectedHTTPStatusError struct {
Status string Status string
} }
func (e *UnexpectedHttpStatusError) Error() string { func (e *UnexpectedHTTPStatusError) Error() string {
return fmt.Sprintf("Received unexpected http status: %s", e.Status) return fmt.Sprintf("Received unexpected HTTP status: %s", e.Status)
} }

View file

@ -36,6 +36,7 @@ type ImageManifest struct {
// imageManifest is used to avoid recursion in unmarshaling // imageManifest is used to avoid recursion in unmarshaling
type imageManifest ImageManifest type imageManifest ImageManifest
// UnmarshalJSON populates a new ImageManifest struct from JSON data.
func (m *ImageManifest) UnmarshalJSON(b []byte) error { func (m *ImageManifest) UnmarshalJSON(b []byte) error {
var manifest imageManifest var manifest imageManifest
err := json.Unmarshal(b, &manifest) err := json.Unmarshal(b, &manifest)

View file

@ -1,3 +1,3 @@
// This package contains storage services for use in the registry application. // Package storage contains storage services for use in the registry
// It should be considered an internal package, as of Go 1.4. // application. It should be considered an internal package, as of Go 1.4.
package storage package storage

View file

@ -11,11 +11,11 @@ import (
"github.com/docker/docker-registry/storagedriver/factory" "github.com/docker/docker-registry/storagedriver/factory"
) )
const DriverName = "filesystem" const driverName = "filesystem"
const DefaultRootDirectory = "/tmp/registry/storage" const defaultRootDirectory = "/tmp/registry/storage"
func init() { func init() {
factory.Register(DriverName, &filesystemDriverFactory{}) factory.Register(driverName, &filesystemDriverFactory{})
} }
// filesystemDriverFactory implements the factory.StorageDriverFactory interface // filesystemDriverFactory implements the factory.StorageDriverFactory interface
@ -25,17 +25,17 @@ func (factory *filesystemDriverFactory) Create(parameters map[string]string) (st
return FromParameters(parameters), nil return FromParameters(parameters), nil
} }
// FilesystemDriver is a storagedriver.StorageDriver implementation backed by a local filesystem // Driver is a storagedriver.StorageDriver implementation backed by a local
// All provided paths will be subpaths of the RootDirectory // filesystem. All provided paths will be subpaths of the RootDirectory
type FilesystemDriver struct { type Driver struct {
rootDirectory string rootDirectory string
} }
// FromParameters constructs a new FilesystemDriver with a given parameters map // FromParameters constructs a new Driver with a given parameters map
// Optional Parameters: // Optional Parameters:
// - rootdirectory // - rootdirectory
func FromParameters(parameters map[string]string) *FilesystemDriver { func FromParameters(parameters map[string]string) *Driver {
var rootDirectory = DefaultRootDirectory var rootDirectory = defaultRootDirectory
if parameters != nil { if parameters != nil {
rootDir, ok := parameters["rootdirectory"] rootDir, ok := parameters["rootdirectory"]
if ok { if ok {
@ -45,19 +45,20 @@ func FromParameters(parameters map[string]string) *FilesystemDriver {
return New(rootDirectory) return New(rootDirectory)
} }
// New constructs a new FilesystemDriver with a given rootDirectory // New constructs a new Driver with a given rootDirectory
func New(rootDirectory string) *FilesystemDriver { func New(rootDirectory string) *Driver {
return &FilesystemDriver{rootDirectory} return &Driver{rootDirectory}
} }
// subPath returns the absolute path of a key within the FilesystemDriver's storage // subPath returns the absolute path of a key within the Driver's storage
func (d *FilesystemDriver) subPath(subPath string) string { func (d *Driver) subPath(subPath string) string {
return path.Join(d.rootDirectory, subPath) return path.Join(d.rootDirectory, subPath)
} }
// Implement the storagedriver.StorageDriver interface // Implement the storagedriver.StorageDriver interface
func (d *FilesystemDriver) GetContent(path string) ([]byte, error) { // GetContent retrieves the content stored at "path" as a []byte.
func (d *Driver) GetContent(path string) ([]byte, error) {
contents, err := ioutil.ReadFile(d.subPath(path)) contents, err := ioutil.ReadFile(d.subPath(path))
if err != nil { if err != nil {
return nil, storagedriver.PathNotFoundError{Path: path} return nil, storagedriver.PathNotFoundError{Path: path}
@ -65,7 +66,8 @@ func (d *FilesystemDriver) GetContent(path string) ([]byte, error) {
return contents, nil return contents, nil
} }
func (d *FilesystemDriver) PutContent(subPath string, contents []byte) error { // PutContent stores the []byte content at a location designated by "path".
func (d *Driver) PutContent(subPath string, contents []byte) error {
fullPath := d.subPath(subPath) fullPath := d.subPath(subPath)
parentDir := path.Dir(fullPath) parentDir := path.Dir(fullPath)
err := os.MkdirAll(parentDir, 0755) err := os.MkdirAll(parentDir, 0755)
@ -77,7 +79,9 @@ func (d *FilesystemDriver) PutContent(subPath string, contents []byte) error {
return err return err
} }
func (d *FilesystemDriver) ReadStream(path string, offset uint64) (io.ReadCloser, error) { // ReadStream retrieves an io.ReadCloser for the content stored at "path" with a
// given byte offset.
func (d *Driver) ReadStream(path string, offset uint64) (io.ReadCloser, error) {
file, err := os.OpenFile(d.subPath(path), os.O_RDONLY, 0644) file, err := os.OpenFile(d.subPath(path), os.O_RDONLY, 0644)
if err != nil { if err != nil {
return nil, err return nil, err
@ -95,7 +99,9 @@ func (d *FilesystemDriver) ReadStream(path string, offset uint64) (io.ReadCloser
return file, nil return file, nil
} }
func (d *FilesystemDriver) WriteStream(subPath string, offset, size uint64, reader io.ReadCloser) error { // WriteStream stores the contents of the provided io.ReadCloser at a location
// designated by the given path.
func (d *Driver) WriteStream(subPath string, offset, size uint64, reader io.ReadCloser) error {
defer reader.Close() defer reader.Close()
resumableOffset, err := d.CurrentSize(subPath) resumableOffset, err := d.CurrentSize(subPath)
@ -154,7 +160,9 @@ func (d *FilesystemDriver) WriteStream(subPath string, offset, size uint64, read
return err return err
} }
func (d *FilesystemDriver) CurrentSize(subPath string) (uint64, error) { // CurrentSize retrieves the curernt size in bytes of the object at the given
// path.
func (d *Driver) CurrentSize(subPath string) (uint64, error) {
fullPath := d.subPath(subPath) fullPath := d.subPath(subPath)
fileInfo, err := os.Stat(fullPath) fileInfo, err := os.Stat(fullPath)
@ -166,7 +174,9 @@ func (d *FilesystemDriver) CurrentSize(subPath string) (uint64, error) {
return uint64(fileInfo.Size()), nil return uint64(fileInfo.Size()), nil
} }
func (d *FilesystemDriver) List(subPath string) ([]string, error) { // List returns a list of the objects that are direct descendants of the given
// path.
func (d *Driver) List(subPath string) ([]string, error) {
subPath = strings.TrimRight(subPath, "/") subPath = strings.TrimRight(subPath, "/")
fullPath := d.subPath(subPath) fullPath := d.subPath(subPath)
@ -188,12 +198,15 @@ func (d *FilesystemDriver) List(subPath string) ([]string, error) {
return keys, nil return keys, nil
} }
func (d *FilesystemDriver) Move(sourcePath string, destPath string) error { // Move moves an object stored at sourcePath to destPath, removing the original
// object.
func (d *Driver) Move(sourcePath string, destPath string) error {
err := os.Rename(d.subPath(sourcePath), d.subPath(destPath)) err := os.Rename(d.subPath(sourcePath), d.subPath(destPath))
return err return err
} }
func (d *FilesystemDriver) Delete(subPath string) error { // Delete recursively deletes all objects stored at "path" and its subpaths.
func (d *Driver) Delete(subPath string) error {
fullPath := d.subPath(subPath) fullPath := d.subPath(subPath)
_, err := os.Stat(fullPath) _, err := os.Stat(fullPath)

View file

@ -20,5 +20,5 @@ func init() {
return New(rootDirectory), nil return New(rootDirectory), nil
} }
testsuites.RegisterInProcessSuite(filesystemDriverConstructor, testsuites.NeverSkip) testsuites.RegisterInProcessSuite(filesystemDriverConstructor, testsuites.NeverSkip)
testsuites.RegisterIPCSuite(DriverName, map[string]string{"rootdirectory": rootDirectory}, testsuites.NeverSkip) testsuites.RegisterIPCSuite(driverName, map[string]string{"rootdirectory": rootDirectory}, testsuites.NeverSkip)
} }

View file

@ -13,34 +13,35 @@ import (
"github.com/docker/docker-registry/storagedriver/factory" "github.com/docker/docker-registry/storagedriver/factory"
) )
const DriverName = "inmemory" const driverName = "inmemory"
func init() { func init() {
factory.Register(DriverName, &inMemoryDriverFactory{}) factory.Register(driverName, &inMemoryDriverFactory{})
} }
// inMemoryDriverFacotry implements the factory.StorageDriverFactory interface // inMemoryDriverFacotry implements the factory.StorageDriverFactory interface.
type inMemoryDriverFactory struct{} type inMemoryDriverFactory struct{}
func (factory *inMemoryDriverFactory) Create(parameters map[string]string) (storagedriver.StorageDriver, error) { func (factory *inMemoryDriverFactory) Create(parameters map[string]string) (storagedriver.StorageDriver, error) {
return New(), nil return New(), nil
} }
// InMemoryDriver is a storagedriver.StorageDriver implementation backed by a local map // Driver is a storagedriver.StorageDriver implementation backed by a local map.
// Intended solely for example and testing purposes // Intended solely for example and testing purposes.
type InMemoryDriver struct { type Driver struct {
storage map[string][]byte storage map[string][]byte
mutex sync.RWMutex mutex sync.RWMutex
} }
// New constructs a new InMemoryDriver // New constructs a new Driver.
func New() *InMemoryDriver { func New() *Driver {
return &InMemoryDriver{storage: make(map[string][]byte)} return &Driver{storage: make(map[string][]byte)}
} }
// Implement the storagedriver.StorageDriver interface // Implement the storagedriver.StorageDriver interface.
func (d *InMemoryDriver) GetContent(path string) ([]byte, error) { // GetContent retrieves the content stored at "path" as a []byte.
func (d *Driver) GetContent(path string) ([]byte, error) {
d.mutex.RLock() d.mutex.RLock()
defer d.mutex.RUnlock() defer d.mutex.RUnlock()
contents, ok := d.storage[path] contents, ok := d.storage[path]
@ -50,14 +51,17 @@ func (d *InMemoryDriver) GetContent(path string) ([]byte, error) {
return contents, nil return contents, nil
} }
func (d *InMemoryDriver) PutContent(path string, contents []byte) error { // PutContent stores the []byte content at a location designated by "path".
func (d *Driver) PutContent(path string, contents []byte) error {
d.mutex.Lock() d.mutex.Lock()
defer d.mutex.Unlock() defer d.mutex.Unlock()
d.storage[path] = contents d.storage[path] = contents
return nil return nil
} }
func (d *InMemoryDriver) ReadStream(path string, offset uint64) (io.ReadCloser, error) { // ReadStream retrieves an io.ReadCloser for the content stored at "path" with a
// given byte offset.
func (d *Driver) ReadStream(path string, offset uint64) (io.ReadCloser, error) {
d.mutex.RLock() d.mutex.RLock()
defer d.mutex.RUnlock() defer d.mutex.RUnlock()
contents, err := d.GetContent(path) contents, err := d.GetContent(path)
@ -73,7 +77,9 @@ func (d *InMemoryDriver) ReadStream(path string, offset uint64) (io.ReadCloser,
return ioutil.NopCloser(bytes.NewReader(buf)), nil return ioutil.NopCloser(bytes.NewReader(buf)), nil
} }
func (d *InMemoryDriver) WriteStream(path string, offset, size uint64, reader io.ReadCloser) error { // WriteStream stores the contents of the provided io.ReadCloser at a location
// designated by the given path.
func (d *Driver) WriteStream(path string, offset, size uint64, reader io.ReadCloser) error {
defer reader.Close() defer reader.Close()
d.mutex.RLock() d.mutex.RLock()
defer d.mutex.RUnlock() defer d.mutex.RUnlock()
@ -100,7 +106,9 @@ func (d *InMemoryDriver) WriteStream(path string, offset, size uint64, reader io
return nil return nil
} }
func (d *InMemoryDriver) CurrentSize(path string) (uint64, error) { // CurrentSize retrieves the curernt size in bytes of the object at the given
// path.
func (d *Driver) CurrentSize(path string) (uint64, error) {
d.mutex.RLock() d.mutex.RLock()
defer d.mutex.RUnlock() defer d.mutex.RUnlock()
contents, ok := d.storage[path] contents, ok := d.storage[path]
@ -110,7 +118,9 @@ func (d *InMemoryDriver) CurrentSize(path string) (uint64, error) {
return uint64(len(contents)), nil return uint64(len(contents)), nil
} }
func (d *InMemoryDriver) List(path string) ([]string, error) { // List returns a list of the objects that are direct descendants of the given
// path.
func (d *Driver) List(path string) ([]string, error) {
subPathMatcher, err := regexp.Compile(fmt.Sprintf("^%s/[^/]+", path)) subPathMatcher, err := regexp.Compile(fmt.Sprintf("^%s/[^/]+", path))
if err != nil { if err != nil {
return nil, err return nil, err
@ -133,7 +143,9 @@ func (d *InMemoryDriver) List(path string) ([]string, error) {
return keys, nil return keys, nil
} }
func (d *InMemoryDriver) Move(sourcePath string, destPath string) error { // Move moves an object stored at sourcePath to destPath, removing the original
// object.
func (d *Driver) Move(sourcePath string, destPath string) error {
d.mutex.Lock() d.mutex.Lock()
defer d.mutex.Unlock() defer d.mutex.Unlock()
contents, ok := d.storage[sourcePath] contents, ok := d.storage[sourcePath]
@ -145,10 +157,11 @@ func (d *InMemoryDriver) Move(sourcePath string, destPath string) error {
return nil return nil
} }
func (d *InMemoryDriver) Delete(path string) error { // Delete recursively deletes all objects stored at "path" and its subpaths.
func (d *Driver) Delete(path string) error {
d.mutex.Lock() d.mutex.Lock()
defer d.mutex.Unlock() defer d.mutex.Unlock()
subPaths := make([]string, 0) var subPaths []string
for k := range d.storage { for k := range d.storage {
if strings.HasPrefix(k, path) { if strings.HasPrefix(k, path) {
subPaths = append(subPaths, k) subPaths = append(subPaths, k)

View file

@ -5,16 +5,17 @@ import (
"github.com/docker/docker-registry/storagedriver" "github.com/docker/docker-registry/storagedriver"
"github.com/docker/docker-registry/storagedriver/testsuites" "github.com/docker/docker-registry/storagedriver/testsuites"
. "gopkg.in/check.v1"
"gopkg.in/check.v1"
) )
// Hook up gocheck into the "go test" runner. // Hook up gocheck into the "go test" runner.
func Test(t *testing.T) { TestingT(t) } func Test(t *testing.T) { check.TestingT(t) }
func init() { func init() {
inmemoryDriverConstructor := func() (storagedriver.StorageDriver, error) { inmemoryDriverConstructor := func() (storagedriver.StorageDriver, error) {
return New(), nil return New(), nil
} }
testsuites.RegisterInProcessSuite(inmemoryDriverConstructor, testsuites.NeverSkip) testsuites.RegisterInProcessSuite(inmemoryDriverConstructor, testsuites.NeverSkip)
testsuites.RegisterIPCSuite(DriverName, nil, testsuites.NeverSkip) testsuites.RegisterIPCSuite(driverName, nil, testsuites.NeverSkip)
} }

View file

@ -173,6 +173,7 @@ func (driver *StorageDriverClient) Stop() error {
// Implement the storagedriver.StorageDriver interface over IPC // Implement the storagedriver.StorageDriver interface over IPC
// GetContent retrieves the content stored at "path" as a []byte.
func (driver *StorageDriverClient) GetContent(path string) ([]byte, error) { func (driver *StorageDriverClient) GetContent(path string) ([]byte, error) {
if err := driver.exited(); err != nil { if err := driver.exited(); err != nil {
return nil, err return nil, err
@ -204,6 +205,7 @@ func (driver *StorageDriverClient) GetContent(path string) ([]byte, error) {
return contents, nil return contents, nil
} }
// PutContent stores the []byte content at a location designated by "path".
func (driver *StorageDriverClient) PutContent(path string, contents []byte) error { func (driver *StorageDriverClient) PutContent(path string, contents []byte) error {
if err := driver.exited(); err != nil { if err := driver.exited(); err != nil {
return err return err
@ -230,6 +232,8 @@ func (driver *StorageDriverClient) PutContent(path string, contents []byte) erro
return nil return nil
} }
// ReadStream retrieves an io.ReadCloser for the content stored at "path" with a
// given byte offset.
func (driver *StorageDriverClient) ReadStream(path string, offset uint64) (io.ReadCloser, error) { func (driver *StorageDriverClient) ReadStream(path string, offset uint64) (io.ReadCloser, error) {
if err := driver.exited(); err != nil { if err := driver.exited(); err != nil {
return nil, err return nil, err
@ -255,6 +259,8 @@ func (driver *StorageDriverClient) ReadStream(path string, offset uint64) (io.Re
return response.Reader, nil return response.Reader, nil
} }
// WriteStream stores the contents of the provided io.ReadCloser at a location
// designated by the given path.
func (driver *StorageDriverClient) WriteStream(path string, offset, size uint64, reader io.ReadCloser) error { func (driver *StorageDriverClient) WriteStream(path string, offset, size uint64, reader io.ReadCloser) error {
if err := driver.exited(); err != nil { if err := driver.exited(); err != nil {
return err return err
@ -280,6 +286,8 @@ func (driver *StorageDriverClient) WriteStream(path string, offset, size uint64,
return nil return nil
} }
// CurrentSize retrieves the curernt size in bytes of the object at the given
// path.
func (driver *StorageDriverClient) CurrentSize(path string) (uint64, error) { func (driver *StorageDriverClient) CurrentSize(path string) (uint64, error) {
if err := driver.exited(); err != nil { if err := driver.exited(); err != nil {
return 0, err return 0, err
@ -305,6 +313,8 @@ func (driver *StorageDriverClient) CurrentSize(path string) (uint64, error) {
return response.Position, nil return response.Position, nil
} }
// List returns a list of the objects that are direct descendants of the given
// path.
func (driver *StorageDriverClient) List(path string) ([]string, error) { func (driver *StorageDriverClient) List(path string) ([]string, error) {
if err := driver.exited(); err != nil { if err := driver.exited(); err != nil {
return nil, err return nil, err
@ -330,6 +340,8 @@ func (driver *StorageDriverClient) List(path string) ([]string, error) {
return response.Keys, nil return response.Keys, nil
} }
// Move moves an object stored at sourcePath to destPath, removing the original
// object.
func (driver *StorageDriverClient) Move(sourcePath string, destPath string) error { func (driver *StorageDriverClient) Move(sourcePath string, destPath string) error {
if err := driver.exited(); err != nil { if err := driver.exited(); err != nil {
return err return err
@ -355,6 +367,7 @@ func (driver *StorageDriverClient) Move(sourcePath string, destPath string) erro
return nil return nil
} }
// Delete recursively deletes all objects stored at "path" and its subpaths.
func (driver *StorageDriverClient) Delete(path string) error { func (driver *StorageDriverClient) Delete(path string) error {
if err := driver.exited(); err != nil { if err := driver.exited(); err != nil {
return err return err

View file

@ -9,10 +9,10 @@ import (
"github.com/docker/libchan" "github.com/docker/libchan"
) )
// IPCStorageDriver is the interface which IPC storage drivers must implement. As external storage // StorageDriver is the interface which IPC storage drivers must implement. As external storage
// drivers may be defined to use a different version of the storagedriver.StorageDriver interface, // drivers may be defined to use a different version of the storagedriver.StorageDriver interface,
// we use an additional version check to determine compatiblity. // we use an additional version check to determine compatiblity.
type IPCStorageDriver interface { type StorageDriver interface {
// Version returns the storagedriver.StorageDriver interface version which this storage driver // Version returns the storagedriver.StorageDriver interface version which this storage driver
// implements, which is used to determine driver compatibility // implements, which is used to determine driver compatibility
Version() (storagedriver.Version, error) Version() (storagedriver.Version, error)
@ -36,23 +36,25 @@ type Request struct {
ResponseChannel libchan.Sender ResponseChannel libchan.Sender
} }
type responseError struct { // ResponseError is a serializable error type.
type ResponseError struct {
Type string Type string
Message string Message string
} }
// ResponseError wraps an error in a serializable struct containing the error's type and message // WrapError wraps an error in a serializable struct containing the error's type
func ResponseError(err error) *responseError { // and message.
func WrapError(err error) *ResponseError {
if err == nil { if err == nil {
return nil return nil
} }
return &responseError{ return &ResponseError{
Type: reflect.TypeOf(err).String(), Type: reflect.TypeOf(err).String(),
Message: err.Error(), Message: err.Error(),
} }
} }
func (err *responseError) Error() string { func (err *ResponseError) Error() string {
return fmt.Sprintf("%s: %s", err.Type, err.Message) return fmt.Sprintf("%s: %s", err.Type, err.Message)
} }
@ -61,38 +63,38 @@ func (err *responseError) Error() string {
// VersionResponse is a response for a Version request // VersionResponse is a response for a Version request
type VersionResponse struct { type VersionResponse struct {
Version storagedriver.Version Version storagedriver.Version
Error *responseError Error *ResponseError
} }
// ReadStreamResponse is a response for a ReadStream request // ReadStreamResponse is a response for a ReadStream request
type ReadStreamResponse struct { type ReadStreamResponse struct {
Reader io.ReadCloser Reader io.ReadCloser
Error *responseError Error *ResponseError
} }
// WriteStreamResponse is a response for a WriteStream request // WriteStreamResponse is a response for a WriteStream request
type WriteStreamResponse struct { type WriteStreamResponse struct {
Error *responseError Error *ResponseError
} }
// CurrentSizeResponse is a response for a CurrentSize request // CurrentSizeResponse is a response for a CurrentSize request
type CurrentSizeResponse struct { type CurrentSizeResponse struct {
Position uint64 Position uint64
Error *responseError Error *ResponseError
} }
// ListResponse is a response for a List request // ListResponse is a response for a List request
type ListResponse struct { type ListResponse struct {
Keys []string Keys []string
Error *responseError Error *ResponseError
} }
// MoveResponse is a response for a Move request // MoveResponse is a response for a Move request
type MoveResponse struct { type MoveResponse struct {
Error *responseError Error *ResponseError
} }
// DeleteResponse is a response for a Delete request // DeleteResponse is a response for a Delete request
type DeleteResponse struct { type DeleteResponse struct {
Error *responseError Error *ResponseError
} }

View file

@ -33,7 +33,9 @@ func StorageDriverServer(driver storagedriver.StorageDriver) error {
} else { } else {
for { for {
receiver, err := transport.WaitReceiveChannel() receiver, err := transport.WaitReceiveChannel()
if err != nil { if err == io.EOF {
return nil
} else if err != nil {
panic(err) panic(err)
} }
go receive(driver, receiver) go receive(driver, receiver)
@ -49,7 +51,9 @@ func receive(driver storagedriver.StorageDriver, receiver libchan.Receiver) {
for { for {
var request Request var request Request
err := receiver.Receive(&request) err := receiver.Receive(&request)
if err != nil { if err == io.EOF {
return
} else if err != nil {
panic(err) panic(err)
} }
go handleRequest(driver, request) go handleRequest(driver, request)
@ -70,7 +74,7 @@ func handleRequest(driver storagedriver.StorageDriver, request Request) {
content, err := driver.GetContent(path) content, err := driver.GetContent(path)
var response ReadStreamResponse var response ReadStreamResponse
if err != nil { if err != nil {
response = ReadStreamResponse{Error: ResponseError(err)} response = ReadStreamResponse{Error: WrapError(err)}
} else { } else {
response = ReadStreamResponse{Reader: ioutil.NopCloser(bytes.NewReader(content))} response = ReadStreamResponse{Reader: ioutil.NopCloser(bytes.NewReader(content))}
} }
@ -87,7 +91,7 @@ func handleRequest(driver storagedriver.StorageDriver, request Request) {
err = driver.PutContent(path, contents) err = driver.PutContent(path, contents)
} }
response := WriteStreamResponse{ response := WriteStreamResponse{
Error: ResponseError(err), Error: WrapError(err),
} }
err = request.ResponseChannel.Send(&response) err = request.ResponseChannel.Send(&response)
if err != nil { if err != nil {
@ -100,7 +104,7 @@ func handleRequest(driver storagedriver.StorageDriver, request Request) {
reader, err := driver.ReadStream(path, offset) reader, err := driver.ReadStream(path, offset)
var response ReadStreamResponse var response ReadStreamResponse
if err != nil { if err != nil {
response = ReadStreamResponse{Error: ResponseError(err)} response = ReadStreamResponse{Error: WrapError(err)}
} else { } else {
response = ReadStreamResponse{Reader: ioutil.NopCloser(reader)} response = ReadStreamResponse{Reader: ioutil.NopCloser(reader)}
} }
@ -117,7 +121,7 @@ func handleRequest(driver storagedriver.StorageDriver, request Request) {
reader, _ := request.Parameters["Reader"].(io.ReadCloser) reader, _ := request.Parameters["Reader"].(io.ReadCloser)
err := driver.WriteStream(path, offset, size, reader) err := driver.WriteStream(path, offset, size, reader)
response := WriteStreamResponse{ response := WriteStreamResponse{
Error: ResponseError(err), Error: WrapError(err),
} }
err = request.ResponseChannel.Send(&response) err = request.ResponseChannel.Send(&response)
if err != nil { if err != nil {
@ -128,7 +132,7 @@ func handleRequest(driver storagedriver.StorageDriver, request Request) {
position, err := driver.CurrentSize(path) position, err := driver.CurrentSize(path)
response := CurrentSizeResponse{ response := CurrentSizeResponse{
Position: position, Position: position,
Error: ResponseError(err), Error: WrapError(err),
} }
err = request.ResponseChannel.Send(&response) err = request.ResponseChannel.Send(&response)
if err != nil { if err != nil {
@ -139,7 +143,7 @@ func handleRequest(driver storagedriver.StorageDriver, request Request) {
keys, err := driver.List(path) keys, err := driver.List(path)
response := ListResponse{ response := ListResponse{
Keys: keys, Keys: keys,
Error: ResponseError(err), Error: WrapError(err),
} }
err = request.ResponseChannel.Send(&response) err = request.ResponseChannel.Send(&response)
if err != nil { if err != nil {
@ -150,7 +154,7 @@ func handleRequest(driver storagedriver.StorageDriver, request Request) {
destPath, _ := request.Parameters["DestPath"].(string) destPath, _ := request.Parameters["DestPath"].(string)
err := driver.Move(sourcePath, destPath) err := driver.Move(sourcePath, destPath)
response := MoveResponse{ response := MoveResponse{
Error: ResponseError(err), Error: WrapError(err),
} }
err = request.ResponseChannel.Send(&response) err = request.ResponseChannel.Send(&response)
if err != nil { if err != nil {
@ -160,7 +164,7 @@ func handleRequest(driver storagedriver.StorageDriver, request Request) {
path, _ := request.Parameters["Path"].(string) path, _ := request.Parameters["Path"].(string)
err := driver.Delete(path) err := driver.Delete(path)
response := DeleteResponse{ response := DeleteResponse{
Error: ResponseError(err), Error: WrapError(err),
} }
err = request.ResponseChannel.Send(&response) err = request.ResponseChannel.Send(&response)
if err != nil { if err != nil {

View file

@ -13,7 +13,7 @@ import (
"github.com/docker/docker-registry/storagedriver/factory" "github.com/docker/docker-registry/storagedriver/factory"
) )
const DriverName = "s3" const driverName = "s3"
// minChunkSize defines the minimum multipart upload chunk size // minChunkSize defines the minimum multipart upload chunk size
// S3 API requires multipart upload chunks to be at least 5MB // S3 API requires multipart upload chunks to be at least 5MB
@ -23,7 +23,7 @@ const minChunkSize = uint64(5 * 1024 * 1024)
const listPartsMax = 1000 const listPartsMax = 1000
func init() { func init() {
factory.Register(DriverName, &s3DriverFactory{}) factory.Register(driverName, &s3DriverFactory{})
} }
// s3DriverFactory implements the factory.StorageDriverFactory interface // s3DriverFactory implements the factory.StorageDriverFactory interface
@ -33,22 +33,22 @@ func (factory *s3DriverFactory) Create(parameters map[string]string) (storagedri
return FromParameters(parameters) return FromParameters(parameters)
} }
// S3Driver is a storagedriver.StorageDriver implementation backed by Amazon S3 // Driver is a storagedriver.StorageDriver implementation backed by Amazon S3
// Objects are stored at absolute keys in the provided bucket // Objects are stored at absolute keys in the provided bucket
type S3Driver struct { type Driver struct {
S3 *s3.S3 S3 *s3.S3
Bucket *s3.Bucket Bucket *s3.Bucket
Encrypt bool Encrypt bool
} }
// FromParameters constructs a new S3Driver with a given parameters map // FromParameters constructs a new Driver with a given parameters map
// Required parameters: // Required parameters:
// - accesskey // - accesskey
// - secretkey // - secretkey
// - region // - region
// - bucket // - bucket
// - encrypt // - encrypt
func FromParameters(parameters map[string]string) (*S3Driver, error) { func FromParameters(parameters map[string]string) (*Driver, error) {
accessKey, ok := parameters["accesskey"] accessKey, ok := parameters["accesskey"]
if !ok || accessKey == "" { if !ok || accessKey == "" {
return nil, fmt.Errorf("No accesskey parameter provided") return nil, fmt.Errorf("No accesskey parameter provided")
@ -85,9 +85,9 @@ func FromParameters(parameters map[string]string) (*S3Driver, error) {
return New(accessKey, secretKey, region, encryptBool, bucket) return New(accessKey, secretKey, region, encryptBool, bucket)
} }
// New constructs a new S3Driver with the given AWS credentials, region, encryption flag, and // New constructs a new Driver with the given AWS credentials, region, encryption flag, and
// bucketName // bucketName
func New(accessKey string, secretKey string, region aws.Region, encrypt bool, bucketName string) (*S3Driver, error) { func New(accessKey string, secretKey string, region aws.Region, encrypt bool, bucketName string) (*Driver, error) {
auth := aws.Auth{AccessKey: accessKey, SecretKey: secretKey} auth := aws.Auth{AccessKey: accessKey, SecretKey: secretKey}
s3obj := s3.New(auth, region) s3obj := s3.New(auth, region)
bucket := s3obj.Bucket(bucketName) bucket := s3obj.Bucket(bucketName)
@ -99,20 +99,24 @@ func New(accessKey string, secretKey string, region aws.Region, encrypt bool, bu
} }
} }
return &S3Driver{s3obj, bucket, encrypt}, nil return &Driver{s3obj, bucket, encrypt}, nil
} }
// Implement the storagedriver.StorageDriver interface // Implement the storagedriver.StorageDriver interface
func (d *S3Driver) GetContent(path string) ([]byte, error) { // GetContent retrieves the content stored at "path" as a []byte.
func (d *Driver) GetContent(path string) ([]byte, error) {
return d.Bucket.Get(path) return d.Bucket.Get(path)
} }
func (d *S3Driver) PutContent(path string, contents []byte) error { // PutContent stores the []byte content at a location designated by "path".
func (d *Driver) PutContent(path string, contents []byte) error {
return d.Bucket.Put(path, contents, d.getContentType(), getPermissions(), d.getOptions()) return d.Bucket.Put(path, contents, d.getContentType(), getPermissions(), d.getOptions())
} }
func (d *S3Driver) ReadStream(path string, offset uint64) (io.ReadCloser, error) { // ReadStream retrieves an io.ReadCloser for the content stored at "path" with a
// given byte offset.
func (d *Driver) ReadStream(path string, offset uint64) (io.ReadCloser, error) {
headers := make(http.Header) headers := make(http.Header)
headers.Add("Range", "bytes="+strconv.FormatUint(offset, 10)+"-") headers.Add("Range", "bytes="+strconv.FormatUint(offset, 10)+"-")
@ -124,7 +128,9 @@ func (d *S3Driver) ReadStream(path string, offset uint64) (io.ReadCloser, error)
return nil, err return nil, err
} }
func (d *S3Driver) WriteStream(path string, offset, size uint64, reader io.ReadCloser) error { // WriteStream stores the contents of the provided io.ReadCloser at a location
// designated by the given path.
func (d *Driver) WriteStream(path string, offset, size uint64, reader io.ReadCloser) error {
defer reader.Close() defer reader.Close()
chunkSize := minChunkSize chunkSize := minChunkSize
@ -177,7 +183,9 @@ func (d *S3Driver) WriteStream(path string, offset, size uint64, reader io.ReadC
return nil return nil
} }
func (d *S3Driver) CurrentSize(path string) (uint64, error) { // CurrentSize retrieves the curernt size in bytes of the object at the given
// path.
func (d *Driver) CurrentSize(path string) (uint64, error) {
_, parts, err := d.getAllParts(path) _, parts, err := d.getAllParts(path)
if err != nil { if err != nil {
return 0, err return 0, err
@ -190,7 +198,9 @@ func (d *S3Driver) CurrentSize(path string) (uint64, error) {
return (((uint64(len(parts)) - 1) * uint64(parts[0].Size)) + uint64(parts[len(parts)-1].Size)), nil return (((uint64(len(parts)) - 1) * uint64(parts[0].Size)) + uint64(parts[len(parts)-1].Size)), nil
} }
func (d *S3Driver) List(path string) ([]string, error) { // List returns a list of the objects that are direct descendants of the given
// path.
func (d *Driver) List(path string) ([]string, error) {
if path[len(path)-1] != '/' { if path[len(path)-1] != '/' {
path = path + "/" path = path + "/"
} }
@ -224,7 +234,9 @@ func (d *S3Driver) List(path string) ([]string, error) {
return append(files, directories...), nil return append(files, directories...), nil
} }
func (d *S3Driver) Move(sourcePath string, destPath string) error { // Move moves an object stored at sourcePath to destPath, removing the original
// object.
func (d *Driver) Move(sourcePath string, destPath string) error {
/* This is terrible, but aws doesn't have an actual move. */ /* This is terrible, but aws doesn't have an actual move. */
_, err := d.Bucket.PutCopy(destPath, getPermissions(), _, err := d.Bucket.PutCopy(destPath, getPermissions(),
s3.CopyOptions{Options: d.getOptions(), MetadataDirective: "", ContentType: d.getContentType()}, s3.CopyOptions{Options: d.getOptions(), MetadataDirective: "", ContentType: d.getContentType()},
@ -236,7 +248,8 @@ func (d *S3Driver) Move(sourcePath string, destPath string) error {
return d.Delete(sourcePath) return d.Delete(sourcePath)
} }
func (d *S3Driver) Delete(path string) error { // Delete recursively deletes all objects stored at "path" and its subpaths.
func (d *Driver) Delete(path string) error {
listResponse, err := d.Bucket.List(path, "", "", listPartsMax) listResponse, err := d.Bucket.List(path, "", "", listPartsMax)
if err != nil || len(listResponse.Contents) == 0 { if err != nil || len(listResponse.Contents) == 0 {
return storagedriver.PathNotFoundError{Path: path} return storagedriver.PathNotFoundError{Path: path}
@ -263,30 +276,29 @@ func (d *S3Driver) Delete(path string) error {
return nil return nil
} }
func (d *S3Driver) getHighestIdMulti(path string) (multi *s3.Multi, err error) { func (d *Driver) getHighestIDMulti(path string) (multi *s3.Multi, err error) {
multis, _, err := d.Bucket.ListMulti(path, "") multis, _, err := d.Bucket.ListMulti(path, "")
if err != nil && !hasCode(err, "NoSuchUpload") { if err != nil && !hasCode(err, "NoSuchUpload") {
return nil, err return nil, err
} }
uploadId := "" uploadID := ""
if len(multis) > 0 { if len(multis) > 0 {
for _, m := range multis { for _, m := range multis {
if m.Key == path && m.UploadId >= uploadId { if m.Key == path && m.UploadId >= uploadID {
uploadId = m.UploadId uploadID = m.UploadId
multi = m multi = m
} }
} }
return multi, nil return multi, nil
} else {
multi, err := d.Bucket.InitMulti(path, d.getContentType(), getPermissions(), d.getOptions())
return multi, err
} }
multi, err = d.Bucket.InitMulti(path, d.getContentType(), getPermissions(), d.getOptions())
return multi, err
} }
func (d *S3Driver) getAllParts(path string) (*s3.Multi, []s3.Part, error) { func (d *Driver) getAllParts(path string) (*s3.Multi, []s3.Part, error) {
multi, err := d.getHighestIdMulti(path) multi, err := d.getHighestIDMulti(path)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@ -300,7 +312,7 @@ func hasCode(err error, code string) bool {
return ok && s3err.Code == code return ok && s3err.Code == code
} }
func (d *S3Driver) getOptions() s3.Options { func (d *Driver) getOptions() s3.Options {
return s3.Options{SSE: d.Encrypt} return s3.Options{SSE: d.Encrypt}
} }
@ -308,6 +320,6 @@ func getPermissions() s3.ACL {
return s3.Private return s3.Private
} }
func (d *S3Driver) getContentType() string { func (d *Driver) getContentType() string {
return "application/octet-stream" return "application/octet-stream"
} }

View file

@ -8,11 +8,12 @@ import (
"github.com/crowdmob/goamz/aws" "github.com/crowdmob/goamz/aws"
"github.com/docker/docker-registry/storagedriver" "github.com/docker/docker-registry/storagedriver"
"github.com/docker/docker-registry/storagedriver/testsuites" "github.com/docker/docker-registry/storagedriver/testsuites"
. "gopkg.in/check.v1"
"gopkg.in/check.v1"
) )
// Hook up gocheck into the "go test" runner. // Hook up gocheck into the "go test" runner.
func Test(t *testing.T) { TestingT(t) } func Test(t *testing.T) { check.TestingT(t) }
func init() { func init() {
accessKey := os.Getenv("AWS_ACCESS_KEY") accessKey := os.Getenv("AWS_ACCESS_KEY")
@ -38,7 +39,7 @@ func init() {
} }
testsuites.RegisterInProcessSuite(s3DriverConstructor, skipCheck) testsuites.RegisterInProcessSuite(s3DriverConstructor, skipCheck)
testsuites.RegisterIPCSuite(DriverName, map[string]string{ testsuites.RegisterIPCSuite(driverName, map[string]string{
"accesskey": accessKey, "accesskey": accessKey,
"secretkey": secretKey, "secretkey": secretKey,
"region": region, "region": region,

View file

@ -7,67 +7,73 @@ import (
"strings" "strings"
) )
// Version is a string representing the storage driver version, of the form Major.Minor. // Version is a string representing the storage driver version, of the form
// The registry must accept storage drivers with equal major version and greater minor version, // Major.Minor.
// but may not be compatible with older storage driver versions. // The registry must accept storage drivers with equal major version and greater
// minor version, but may not be compatible with older storage driver versions.
type Version string type Version string
// Major returns the major (primary) component of a version // Major returns the major (primary) component of a version.
func (version Version) Major() uint { func (version Version) Major() uint {
majorPart := strings.Split(string(version), ".")[0] majorPart := strings.Split(string(version), ".")[0]
major, _ := strconv.ParseUint(majorPart, 10, 0) major, _ := strconv.ParseUint(majorPart, 10, 0)
return uint(major) return uint(major)
} }
// Minor returns the minor (secondary) component of a version // Minor returns the minor (secondary) component of a version.
func (version Version) Minor() uint { func (version Version) Minor() uint {
minorPart := strings.Split(string(version), ".")[1] minorPart := strings.Split(string(version), ".")[1]
minor, _ := strconv.ParseUint(minorPart, 10, 0) minor, _ := strconv.ParseUint(minorPart, 10, 0)
return uint(minor) return uint(minor)
} }
// CurrentVersion is the current storage driver Version // CurrentVersion is the current storage driver Version.
const CurrentVersion Version = "0.1" const CurrentVersion Version = "0.1"
// StorageDriver defines methods that a Storage Driver must implement for a filesystem-like // StorageDriver defines methods that a Storage Driver must implement for a
// key/value object storage // filesystem-like key/value object storage.
type StorageDriver interface { type StorageDriver interface {
// GetContent retrieves the content stored at "path" as a []byte // GetContent retrieves the content stored at "path" as a []byte.
// Should primarily be used for small objects // This should primarily be used for small objects.
GetContent(path string) ([]byte, error) GetContent(path string) ([]byte, error)
// PutContent stores the []byte content at a location designated by "path" // PutContent stores the []byte content at a location designated by "path".
// Should primarily be used for small objects // This should primarily be used for small objects.
PutContent(path string, content []byte) error PutContent(path string, content []byte) error
// ReadStream retrieves an io.ReadCloser for the content stored at "path" with a given byte // ReadStream retrieves an io.ReadCloser for the content stored at "path"
// offset // with a given byte offset.
// May be used to resume reading a stream by providing a nonzero offset // May be used to resume reading a stream by providing a nonzero offset.
ReadStream(path string, offset uint64) (io.ReadCloser, error) ReadStream(path string, offset uint64) (io.ReadCloser, error)
// WriteStream stores the contents of the provided io.ReadCloser at a location designated by // WriteStream stores the contents of the provided io.ReadCloser at a
// the given path // location designated by the given path.
// The driver will know it has received the full contents when it has read "size" bytes // The driver will know it has received the full contents when it has read
// May be used to resume writing a stream by providing a nonzero offset // "size" bytes.
// The offset must be no larger than the CurrentSize for this path // May be used to resume writing a stream by providing a nonzero offset.
// The offset must be no larger than the CurrentSize for this path.
WriteStream(path string, offset, size uint64, readCloser io.ReadCloser) error WriteStream(path string, offset, size uint64, readCloser io.ReadCloser) error
// CurrentSize retrieves the curernt size in bytes of the object at the given path // CurrentSize retrieves the curernt size in bytes of the object at the
// It should be safe to read or write anywhere up to this point // given path.
// It should be safe to read or write anywhere up to this point.
CurrentSize(path string) (uint64, error) CurrentSize(path string) (uint64, error)
// List returns a list of the objects that are direct descendants of the given path // List returns a list of the objects that are direct descendants of the
//given path.
List(path string) ([]string, error) List(path string) ([]string, error)
// Move moves an object stored at sourcePath to destPath, removing the original object // Move moves an object stored at sourcePath to destPath, removing the
// Note: This may be no more efficient than a copy followed by a delete for many implementations // original object.
// Note: This may be no more efficient than a copy followed by a delete for
// many implementations.
Move(sourcePath string, destPath string) error Move(sourcePath string, destPath string) error
// Delete recursively deletes all objects stored at "path" and its subpaths // Delete recursively deletes all objects stored at "path" and its subpaths.
Delete(path string) error Delete(path string) error
} }
// PathNotFoundError is returned when operating on a nonexistent path // PathNotFoundError is returned when operating on a nonexistent path.
type PathNotFoundError struct { type PathNotFoundError struct {
Path string Path string
} }
@ -76,7 +82,8 @@ func (err PathNotFoundError) Error() string {
return fmt.Sprintf("Path not found: %s", err.Path) return fmt.Sprintf("Path not found: %s", err.Path)
} }
// InvalidOffsetError is returned when attempting to read or write from an invalid offset // InvalidOffsetError is returned when attempting to read or write from an
// invalid offset.
type InvalidOffsetError struct { type InvalidOffsetError struct {
Path string Path string
Offset uint64 Offset uint64

View file

@ -11,15 +11,15 @@ import (
"github.com/docker/docker-registry/storagedriver" "github.com/docker/docker-registry/storagedriver"
"github.com/docker/docker-registry/storagedriver/ipc" "github.com/docker/docker-registry/storagedriver/ipc"
. "gopkg.in/check.v1" "gopkg.in/check.v1"
) )
// Hook up gocheck into the "go test" runner // Test hooks up gocheck into the "go test" runner.
func Test(t *testing.T) { TestingT(t) } func Test(t *testing.T) { check.TestingT(t) }
// RegisterInProcessSuite registers an in-process storage driver test suite with the go test runner // RegisterInProcessSuite registers an in-process storage driver test suite with the go test runner
func RegisterInProcessSuite(driverConstructor DriverConstructor, skipCheck SkipCheck) { func RegisterInProcessSuite(driverConstructor DriverConstructor, skipCheck SkipCheck) {
Suite(&DriverSuite{ check.Suite(&DriverSuite{
Constructor: driverConstructor, Constructor: driverConstructor,
SkipCheck: skipCheck, SkipCheck: skipCheck,
}) })
@ -50,7 +50,7 @@ func RegisterIPCSuite(driverName string, ipcParams map[string]string, skipCheck
driverClient := suite.StorageDriver.(*ipc.StorageDriverClient) driverClient := suite.StorageDriver.(*ipc.StorageDriverClient)
return driverClient.Stop() return driverClient.Stop()
} }
Suite(suite) check.Suite(suite)
} }
// SkipCheck is a function used to determine if a test suite should be skipped // SkipCheck is a function used to determine if a test suite should be skipped
@ -75,77 +75,93 @@ type DriverSuite struct {
storagedriver.StorageDriver storagedriver.StorageDriver
} }
func (suite *DriverSuite) SetUpSuite(c *C) { // SetUpSuite sets up the gocheck test suite
func (suite *DriverSuite) SetUpSuite(c *check.C) {
if reason := suite.SkipCheck(); reason != "" { if reason := suite.SkipCheck(); reason != "" {
c.Skip(reason) c.Skip(reason)
} }
d, err := suite.Constructor() d, err := suite.Constructor()
c.Assert(err, IsNil) c.Assert(err, check.IsNil)
suite.StorageDriver = d suite.StorageDriver = d
} }
func (suite *DriverSuite) TearDownSuite(c *C) { // TearDownSuite tears down the gocheck test suite
func (suite *DriverSuite) TearDownSuite(c *check.C) {
if suite.Teardown != nil { if suite.Teardown != nil {
err := suite.Teardown() err := suite.Teardown()
c.Assert(err, IsNil) c.Assert(err, check.IsNil)
} }
} }
func (suite *DriverSuite) TestWriteRead1(c *C) { // TestWriteRead1 tests a simple write-read workflow
func (suite *DriverSuite) TestWriteRead1(c *check.C) {
filename := randomString(32) filename := randomString(32)
contents := []byte("a") contents := []byte("a")
suite.writeReadCompare(c, filename, contents, contents) suite.writeReadCompare(c, filename, contents, contents)
} }
func (suite *DriverSuite) TestWriteRead2(c *C) { // TestWriteRead2 tests a simple write-read workflow with unicode data
func (suite *DriverSuite) TestWriteRead2(c *check.C) {
filename := randomString(32) filename := randomString(32)
contents := []byte("\xc3\x9f") contents := []byte("\xc3\x9f")
suite.writeReadCompare(c, filename, contents, contents) suite.writeReadCompare(c, filename, contents, contents)
} }
func (suite *DriverSuite) TestWriteRead3(c *C) { // TestWriteRead3 tests a simple write-read workflow with a small string
func (suite *DriverSuite) TestWriteRead3(c *check.C) {
filename := randomString(32) filename := randomString(32)
contents := []byte(randomString(32)) contents := []byte(randomString(32))
suite.writeReadCompare(c, filename, contents, contents) suite.writeReadCompare(c, filename, contents, contents)
} }
func (suite *DriverSuite) TestWriteRead4(c *C) { // TestWriteRead4 tests a simple write-read workflow with 1MB of data
func (suite *DriverSuite) TestWriteRead4(c *check.C) {
filename := randomString(32) filename := randomString(32)
contents := []byte(randomString(1024 * 1024)) contents := []byte(randomString(1024 * 1024))
suite.writeReadCompare(c, filename, contents, contents) suite.writeReadCompare(c, filename, contents, contents)
} }
func (suite *DriverSuite) TestReadNonexistent(c *C) { // TestReadNonexistent tests reading content from an empty path
func (suite *DriverSuite) TestReadNonexistent(c *check.C) {
filename := randomString(32) filename := randomString(32)
_, err := suite.StorageDriver.GetContent(filename) _, err := suite.StorageDriver.GetContent(filename)
c.Assert(err, NotNil) c.Assert(err, check.NotNil)
} }
func (suite *DriverSuite) TestWriteReadStreams1(c *C) { // TestWriteReadStreams1 tests a simple write-read streaming workflow
func (suite *DriverSuite) TestWriteReadStreams1(c *check.C) {
filename := randomString(32) filename := randomString(32)
contents := []byte("a") contents := []byte("a")
suite.writeReadCompareStreams(c, filename, contents, contents) suite.writeReadCompareStreams(c, filename, contents, contents)
} }
func (suite *DriverSuite) TestWriteReadStreams2(c *C) { // TestWriteReadStreams2 tests a simple write-read streaming workflow with
// unicode data
func (suite *DriverSuite) TestWriteReadStreams2(c *check.C) {
filename := randomString(32) filename := randomString(32)
contents := []byte("\xc3\x9f") contents := []byte("\xc3\x9f")
suite.writeReadCompareStreams(c, filename, contents, contents) suite.writeReadCompareStreams(c, filename, contents, contents)
} }
func (suite *DriverSuite) TestWriteReadStreams3(c *C) { // TestWriteReadStreams3 tests a simple write-read streaming workflow with a
// small amount of data
func (suite *DriverSuite) TestWriteReadStreams3(c *check.C) {
filename := randomString(32) filename := randomString(32)
contents := []byte(randomString(32)) contents := []byte(randomString(32))
suite.writeReadCompareStreams(c, filename, contents, contents) suite.writeReadCompareStreams(c, filename, contents, contents)
} }
func (suite *DriverSuite) TestWriteReadStreams4(c *C) { // TestWriteReadStreams4 tests a simple write-read streaming workflow with 1MB
// of data
func (suite *DriverSuite) TestWriteReadStreams4(c *check.C) {
filename := randomString(32) filename := randomString(32)
contents := []byte(randomString(1024 * 1024)) contents := []byte(randomString(1024 * 1024))
suite.writeReadCompareStreams(c, filename, contents, contents) suite.writeReadCompareStreams(c, filename, contents, contents)
} }
func (suite *DriverSuite) TestContinueStreamAppend(c *C) { // TestContinueStreamAppend tests that a stream write can be appended to without
// corrupting the data
func (suite *DriverSuite) TestContinueStreamAppend(c *check.C) {
filename := randomString(32) filename := randomString(32)
defer suite.StorageDriver.Delete(filename) defer suite.StorageDriver.Delete(filename)
@ -158,31 +174,33 @@ func (suite *DriverSuite) TestContinueStreamAppend(c *C) {
fullContents := append(append(contentsChunk1, contentsChunk2...), contentsChunk3...) fullContents := append(append(contentsChunk1, contentsChunk2...), contentsChunk3...)
err := suite.StorageDriver.WriteStream(filename, 0, 3*chunkSize, ioutil.NopCloser(bytes.NewReader(contentsChunk1))) err := suite.StorageDriver.WriteStream(filename, 0, 3*chunkSize, ioutil.NopCloser(bytes.NewReader(contentsChunk1)))
c.Assert(err, IsNil) c.Assert(err, check.IsNil)
offset, err := suite.StorageDriver.CurrentSize(filename) offset, err := suite.StorageDriver.CurrentSize(filename)
c.Assert(err, IsNil) c.Assert(err, check.IsNil)
if offset > chunkSize { if offset > chunkSize {
c.Fatalf("Offset too large, %d > %d", 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]))) err = suite.StorageDriver.WriteStream(filename, offset, 3*chunkSize, ioutil.NopCloser(bytes.NewReader(fullContents[offset:2*chunkSize])))
c.Assert(err, IsNil) c.Assert(err, check.IsNil)
offset, err = suite.StorageDriver.CurrentSize(filename) offset, err = suite.StorageDriver.CurrentSize(filename)
c.Assert(err, IsNil) c.Assert(err, check.IsNil)
if offset > 2*chunkSize { if offset > 2*chunkSize {
c.Fatalf("Offset too large, %d > %d", 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:]))) err = suite.StorageDriver.WriteStream(filename, offset, 3*chunkSize, ioutil.NopCloser(bytes.NewReader(fullContents[offset:])))
c.Assert(err, IsNil) c.Assert(err, check.IsNil)
received, err := suite.StorageDriver.GetContent(filename) received, err := suite.StorageDriver.GetContent(filename)
c.Assert(err, IsNil) c.Assert(err, check.IsNil)
c.Assert(received, DeepEquals, fullContents) c.Assert(received, check.DeepEquals, fullContents)
} }
func (suite *DriverSuite) TestReadStreamWithOffset(c *C) { // TestReadStreamWithOffset tests that the appropriate data is streamed when
// reading with a given offset
func (suite *DriverSuite) TestReadStreamWithOffset(c *check.C) {
filename := randomString(32) filename := randomString(32)
defer suite.StorageDriver.Delete(filename) defer suite.StorageDriver.Delete(filename)
@ -193,43 +211,46 @@ func (suite *DriverSuite) TestReadStreamWithOffset(c *C) {
contentsChunk3 := []byte(randomString(chunkSize)) contentsChunk3 := []byte(randomString(chunkSize))
err := suite.StorageDriver.PutContent(filename, append(append(contentsChunk1, contentsChunk2...), contentsChunk3...)) err := suite.StorageDriver.PutContent(filename, append(append(contentsChunk1, contentsChunk2...), contentsChunk3...))
c.Assert(err, IsNil) c.Assert(err, check.IsNil)
reader, err := suite.StorageDriver.ReadStream(filename, 0) reader, err := suite.StorageDriver.ReadStream(filename, 0)
c.Assert(err, IsNil) c.Assert(err, check.IsNil)
defer reader.Close() defer reader.Close()
readContents, err := ioutil.ReadAll(reader) readContents, err := ioutil.ReadAll(reader)
c.Assert(err, IsNil) c.Assert(err, check.IsNil)
c.Assert(readContents, DeepEquals, append(append(contentsChunk1, contentsChunk2...), contentsChunk3...)) c.Assert(readContents, check.DeepEquals, append(append(contentsChunk1, contentsChunk2...), contentsChunk3...))
reader, err = suite.StorageDriver.ReadStream(filename, chunkSize) reader, err = suite.StorageDriver.ReadStream(filename, chunkSize)
c.Assert(err, IsNil) c.Assert(err, check.IsNil)
defer reader.Close() defer reader.Close()
readContents, err = ioutil.ReadAll(reader) readContents, err = ioutil.ReadAll(reader)
c.Assert(err, IsNil) c.Assert(err, check.IsNil)
c.Assert(readContents, DeepEquals, append(contentsChunk2, contentsChunk3...)) c.Assert(readContents, check.DeepEquals, append(contentsChunk2, contentsChunk3...))
reader, err = suite.StorageDriver.ReadStream(filename, chunkSize*2) reader, err = suite.StorageDriver.ReadStream(filename, chunkSize*2)
c.Assert(err, IsNil) c.Assert(err, check.IsNil)
defer reader.Close() defer reader.Close()
readContents, err = ioutil.ReadAll(reader) readContents, err = ioutil.ReadAll(reader)
c.Assert(err, IsNil) c.Assert(err, check.IsNil)
c.Assert(readContents, DeepEquals, contentsChunk3) c.Assert(readContents, check.DeepEquals, contentsChunk3)
} }
func (suite *DriverSuite) TestReadNonexistentStream(c *C) { // TestReadNonexistentStream tests that reading a stream for a nonexistent path
// fails
func (suite *DriverSuite) TestReadNonexistentStream(c *check.C) {
filename := randomString(32) filename := randomString(32)
_, err := suite.StorageDriver.ReadStream(filename, 0) _, err := suite.StorageDriver.ReadStream(filename, 0)
c.Assert(err, NotNil) c.Assert(err, check.NotNil)
} }
func (suite *DriverSuite) TestList(c *C) { // TestList checks the returned list of keys after populating a directory tree
func (suite *DriverSuite) TestList(c *check.C) {
rootDirectory := randomString(uint64(8 + rand.Intn(8))) rootDirectory := randomString(uint64(8 + rand.Intn(8)))
defer suite.StorageDriver.Delete(rootDirectory) defer suite.StorageDriver.Delete(rootDirectory)
@ -239,22 +260,24 @@ func (suite *DriverSuite) TestList(c *C) {
childFile := parentDirectory + "/" + randomString(uint64(8+rand.Intn(8))) childFile := parentDirectory + "/" + randomString(uint64(8+rand.Intn(8)))
childFiles[i] = childFile childFiles[i] = childFile
err := suite.StorageDriver.PutContent(childFile, []byte(randomString(32))) err := suite.StorageDriver.PutContent(childFile, []byte(randomString(32)))
c.Assert(err, IsNil) c.Assert(err, check.IsNil)
} }
sort.Strings(childFiles) sort.Strings(childFiles)
keys, err := suite.StorageDriver.List(rootDirectory) keys, err := suite.StorageDriver.List(rootDirectory)
c.Assert(err, IsNil) c.Assert(err, check.IsNil)
c.Assert(keys, DeepEquals, []string{parentDirectory}) c.Assert(keys, check.DeepEquals, []string{parentDirectory})
keys, err = suite.StorageDriver.List(parentDirectory) keys, err = suite.StorageDriver.List(parentDirectory)
c.Assert(err, IsNil) c.Assert(err, check.IsNil)
sort.Strings(keys) sort.Strings(keys)
c.Assert(keys, DeepEquals, childFiles) c.Assert(keys, check.DeepEquals, childFiles)
} }
func (suite *DriverSuite) TestMove(c *C) { // TestMove checks that a moved object no longer exists at the source path and
// does exist at the destination
func (suite *DriverSuite) TestMove(c *check.C) {
contents := []byte(randomString(32)) contents := []byte(randomString(32))
sourcePath := randomString(32) sourcePath := randomString(32)
destPath := randomString(32) destPath := randomString(32)
@ -263,50 +286,55 @@ func (suite *DriverSuite) TestMove(c *C) {
defer suite.StorageDriver.Delete(destPath) defer suite.StorageDriver.Delete(destPath)
err := suite.StorageDriver.PutContent(sourcePath, contents) err := suite.StorageDriver.PutContent(sourcePath, contents)
c.Assert(err, IsNil) c.Assert(err, check.IsNil)
err = suite.StorageDriver.Move(sourcePath, destPath) err = suite.StorageDriver.Move(sourcePath, destPath)
c.Assert(err, IsNil) c.Assert(err, check.IsNil)
received, err := suite.StorageDriver.GetContent(destPath) received, err := suite.StorageDriver.GetContent(destPath)
c.Assert(err, IsNil) c.Assert(err, check.IsNil)
c.Assert(received, DeepEquals, contents) c.Assert(received, check.DeepEquals, contents)
_, err = suite.StorageDriver.GetContent(sourcePath) _, err = suite.StorageDriver.GetContent(sourcePath)
c.Assert(err, NotNil) c.Assert(err, check.NotNil)
} }
func (suite *DriverSuite) TestMoveNonexistent(c *C) { // TestMoveNonexistent checks that moving a nonexistent key fails
func (suite *DriverSuite) TestMoveNonexistent(c *check.C) {
sourcePath := randomString(32) sourcePath := randomString(32)
destPath := randomString(32) destPath := randomString(32)
err := suite.StorageDriver.Move(sourcePath, destPath) err := suite.StorageDriver.Move(sourcePath, destPath)
c.Assert(err, NotNil) c.Assert(err, check.NotNil)
} }
func (suite *DriverSuite) TestRemove(c *C) { // TestDelete checks that the delete operation removes data from the storage
// driver
func (suite *DriverSuite) TestDelete(c *check.C) {
filename := randomString(32) filename := randomString(32)
contents := []byte(randomString(32)) contents := []byte(randomString(32))
defer suite.StorageDriver.Delete(filename) defer suite.StorageDriver.Delete(filename)
err := suite.StorageDriver.PutContent(filename, contents) err := suite.StorageDriver.PutContent(filename, contents)
c.Assert(err, IsNil) c.Assert(err, check.IsNil)
err = suite.StorageDriver.Delete(filename) err = suite.StorageDriver.Delete(filename)
c.Assert(err, IsNil) c.Assert(err, check.IsNil)
_, err = suite.StorageDriver.GetContent(filename) _, err = suite.StorageDriver.GetContent(filename)
c.Assert(err, NotNil) c.Assert(err, check.NotNil)
} }
func (suite *DriverSuite) TestRemoveNonexistent(c *C) { // TestDeleteNonexistent checks that removing a nonexistent key fails
func (suite *DriverSuite) TestDeleteNonexistent(c *check.C) {
filename := randomString(32) filename := randomString(32)
err := suite.StorageDriver.Delete(filename) err := suite.StorageDriver.Delete(filename)
c.Assert(err, NotNil) c.Assert(err, check.NotNil)
} }
func (suite *DriverSuite) TestRemoveFolder(c *C) { // TestDeleteFolder checks that deleting a folder removes all child elements
func (suite *DriverSuite) TestDeleteFolder(c *check.C) {
dirname := randomString(32) dirname := randomString(32)
filename1 := randomString(32) filename1 := randomString(32)
filename2 := randomString(32) filename2 := randomString(32)
@ -316,47 +344,47 @@ func (suite *DriverSuite) TestRemoveFolder(c *C) {
defer suite.StorageDriver.Delete(path.Join(dirname, filename2)) defer suite.StorageDriver.Delete(path.Join(dirname, filename2))
err := suite.StorageDriver.PutContent(path.Join(dirname, filename1), contents) err := suite.StorageDriver.PutContent(path.Join(dirname, filename1), contents)
c.Assert(err, IsNil) c.Assert(err, check.IsNil)
err = suite.StorageDriver.PutContent(path.Join(dirname, filename2), contents) err = suite.StorageDriver.PutContent(path.Join(dirname, filename2), contents)
c.Assert(err, IsNil) c.Assert(err, check.IsNil)
err = suite.StorageDriver.Delete(dirname) err = suite.StorageDriver.Delete(dirname)
c.Assert(err, IsNil) c.Assert(err, check.IsNil)
_, err = suite.StorageDriver.GetContent(path.Join(dirname, filename1)) _, err = suite.StorageDriver.GetContent(path.Join(dirname, filename1))
c.Assert(err, NotNil) c.Assert(err, check.NotNil)
_, err = suite.StorageDriver.GetContent(path.Join(dirname, filename2)) _, err = suite.StorageDriver.GetContent(path.Join(dirname, filename2))
c.Assert(err, NotNil) c.Assert(err, check.NotNil)
} }
func (suite *DriverSuite) writeReadCompare(c *C, filename string, contents, expected []byte) { func (suite *DriverSuite) writeReadCompare(c *check.C, filename string, contents, expected []byte) {
defer suite.StorageDriver.Delete(filename) defer suite.StorageDriver.Delete(filename)
err := suite.StorageDriver.PutContent(filename, contents) err := suite.StorageDriver.PutContent(filename, contents)
c.Assert(err, IsNil) c.Assert(err, check.IsNil)
readContents, err := suite.StorageDriver.GetContent(filename) readContents, err := suite.StorageDriver.GetContent(filename)
c.Assert(err, IsNil) c.Assert(err, check.IsNil)
c.Assert(readContents, DeepEquals, contents) c.Assert(readContents, check.DeepEquals, contents)
} }
func (suite *DriverSuite) writeReadCompareStreams(c *C, filename string, contents, expected []byte) { func (suite *DriverSuite) writeReadCompareStreams(c *check.C, filename string, contents, expected []byte) {
defer suite.StorageDriver.Delete(filename) defer suite.StorageDriver.Delete(filename)
err := suite.StorageDriver.WriteStream(filename, 0, uint64(len(contents)), ioutil.NopCloser(bytes.NewReader(contents))) err := suite.StorageDriver.WriteStream(filename, 0, uint64(len(contents)), ioutil.NopCloser(bytes.NewReader(contents)))
c.Assert(err, IsNil) c.Assert(err, check.IsNil)
reader, err := suite.StorageDriver.ReadStream(filename, 0) reader, err := suite.StorageDriver.ReadStream(filename, 0)
c.Assert(err, IsNil) c.Assert(err, check.IsNil)
defer reader.Close() defer reader.Close()
readContents, err := ioutil.ReadAll(reader) readContents, err := ioutil.ReadAll(reader)
c.Assert(err, IsNil) c.Assert(err, check.IsNil)
c.Assert(readContents, DeepEquals, contents) c.Assert(readContents, check.DeepEquals, contents)
} }
var pathChars = []byte("abcdefghijklmnopqrstuvwxyz") var pathChars = []byte("abcdefghijklmnopqrstuvwxyz")