diff --git a/storagedriver/filesystem/driver.go b/storagedriver/filesystem/driver.go index 7307eae76..cf52b383d 100644 --- a/storagedriver/filesystem/driver.go +++ b/storagedriver/filesystem/driver.go @@ -265,6 +265,12 @@ func (d *Driver) Delete(subPath string) error { return err } +// URLFor returns a URL which may be used to retrieve the content stored at the given path. +// May return an UnsupportedMethodErr in certain StorageDriver implementations. +func (d *Driver) URLFor(path string) (string, error) { + return "", storagedriver.ErrUnsupportedMethod +} + // fullPath returns the absolute path of a key within the Driver's storage. func (d *Driver) fullPath(subPath string) string { return path.Join(d.rootDirectory, subPath) diff --git a/storagedriver/inmemory/driver.go b/storagedriver/inmemory/driver.go index d56ea3a4f..89692623c 100644 --- a/storagedriver/inmemory/driver.go +++ b/storagedriver/inmemory/driver.go @@ -251,3 +251,9 @@ func (d *Driver) Delete(path string) error { return err } } + +// URLFor returns a URL which may be used to retrieve the content stored at the given path. +// May return an UnsupportedMethodErr in certain StorageDriver implementations. +func (d *Driver) URLFor(path string) (string, error) { + return "", storagedriver.ErrUnsupportedMethod +} diff --git a/storagedriver/s3/s3.go b/storagedriver/s3/s3.go index ae036d476..916a42873 100644 --- a/storagedriver/s3/s3.go +++ b/storagedriver/s3/s3.go @@ -580,6 +580,16 @@ func (d *Driver) Delete(path string) error { return nil } +// URLFor returns a URL which may be used to retrieve the content stored at the given path. +// May return an UnsupportedMethodErr in certain StorageDriver implementations. +func (d *Driver) URLFor(path string) (string, error) { + if !storagedriver.PathRegexp.MatchString(path) { + return "", storagedriver.InvalidPathError{Path: path} + } + + return d.Bucket.SignedURL(d.s3Path(path), time.Now().Add(24*time.Hour)), nil +} + func (d *Driver) s3Path(path string) string { return strings.TrimLeft(strings.TrimRight(d.rootDirectory, "/")+path, "/") } diff --git a/storagedriver/storagedriver.go b/storagedriver/storagedriver.go index 8f8990fc1..f5a3ca008 100644 --- a/storagedriver/storagedriver.go +++ b/storagedriver/storagedriver.go @@ -1,6 +1,7 @@ package storagedriver import ( + "errors" "fmt" "io" "regexp" @@ -69,6 +70,10 @@ type StorageDriver interface { // Delete recursively deletes all objects stored at "path" and its subpaths. Delete(path string) error + + // URLFor returns a URL which may be used to retrieve the content stored at the given path. + // May return an UnsupportedMethodErr in certain StorageDriver implementations. + URLFor(path string) (string, error) } // PathRegexp is the regular expression which each file path must match. @@ -78,6 +83,9 @@ type StorageDriver interface { // a period, underscore, or hyphen. var PathRegexp = regexp.MustCompile(`^(/[a-z0-9]+([._-][a-z0-9]+)*)+$`) +// UnsupportedMethodErr may be returned in the case where a StorageDriver implementation does not support an optional method. +var ErrUnsupportedMethod = errors.New("Unsupported method") + // PathNotFoundError is returned when operating on a nonexistent path. type PathNotFoundError struct { Path string diff --git a/storagedriver/testsuites/testsuites.go b/storagedriver/testsuites/testsuites.go index e4df50861..63849019d 100644 --- a/storagedriver/testsuites/testsuites.go +++ b/storagedriver/testsuites/testsuites.go @@ -6,6 +6,7 @@ import ( "io" "io/ioutil" "math/rand" + "net/http" "os" "path" "sort" @@ -580,6 +581,32 @@ func (suite *DriverSuite) TestDelete(c *check.C) { c.Assert(err, check.FitsTypeOf, storagedriver.PathNotFoundError{}) } +// TestURLFor checks that the URLFor method functions properly, but only if it +// is implemented +func (suite *DriverSuite) TestURLFor(c *check.C) { + filename := randomPath(32) + contents := randomContents(32) + + defer suite.StorageDriver.Delete(firstPart(filename)) + + err := suite.StorageDriver.PutContent(filename, contents) + c.Assert(err, check.IsNil) + + url, err := suite.StorageDriver.URLFor(filename) + if err == storagedriver.ErrUnsupportedMethod { + return + } + c.Assert(err, check.IsNil) + + response, err := http.Get(url) + c.Assert(err, check.IsNil) + defer response.Body.Close() + + read, err := ioutil.ReadAll(response.Body) + c.Assert(err, check.IsNil) + c.Assert(read, check.DeepEquals, contents) +} + // TestDeleteNonexistent checks that removing a nonexistent key fails. func (suite *DriverSuite) TestDeleteNonexistent(c *check.C) { filename := randomPath(32)