Delete object version endpoint

This commit is contained in:
shabbyrobe
2019-02-16 18:27:11 +11:00
parent 796336f303
commit ea7c0be062
8 changed files with 71 additions and 19 deletions

View File

@@ -24,6 +24,19 @@ type Object struct {
IsDeleteMarker bool
}
type ObjectDeleteResult struct {
// Specifies whether the versioned object that was permanently deleted was
// (true) or was not (false) a delete marker. In a simple DELETE, this
// header indicates whether (true) or not (false) a delete marker was
// created.
IsDeleteMarker bool
// Returns the version ID of the delete marker created as a result of the
// DELETE operation. If you delete a specific object version, the value
// returned by this header is the version ID of the object version deleted.
VersionID VersionID
}
// Backend provides a set of operations to be implemented in order to support
// gofakes3.
//
@@ -84,6 +97,9 @@ type Backend interface {
// DeleteObject deletes an object from the bucket.
//
// If the backend is a VersionedBackend and versioning is enabled, this
// should introduce a delete marker rather than actually delete the object.
//
// DeleteObject must return a gofakes3.ErrNoSuchBucket error if the bucket
// does not exist. See gofakes3.BucketNotFound() for a convenient way to create one.
//
@@ -94,7 +110,7 @@ type Backend interface {
// delete marker, which becomes the latest version of the object. If there
// isn't a null version, Amazon S3 does not remove any objects.
//
DeleteObject(bucketName, objectName string) error
DeleteObject(bucketName, objectName string) (ObjectDeleteResult, error)
// HeadObject fetches the Object from the backend, but the Contents will be
// a no-op ReadCloser.
@@ -113,7 +129,7 @@ type Backend interface {
DeleteMulti(bucketName string, objects ...string) (DeleteResult, error)
}
// VersionedBucket may be optionally implemented by a Backend in order to support
// VersionedBackend may be optionally implemented by a Backend in order to support
// operations on S3 object versions.
//
// If you don't implement VersionedBackend, requests to GoFakeS3 that attempt to
@@ -133,5 +149,8 @@ type VersionedBackend interface {
versionID VersionID,
rangeRequest *ObjectRangeRequest) (*Object, error)
// DeleteObjectVersion permanently deletes a specific object version.
DeleteObjectVersion(bucketName, objectName string, versionID VersionID) (ObjectDeleteResult, error)
ListBucketVersions(bucketName string, prefix Prefix) (*ListBucketVersionsResult, error)
}

View File

@@ -223,7 +223,7 @@ func TestPutDelete(t *testing.T) {
t.Fatal(err)
}
if err := backend.DeleteObject("test", "foo"); err != nil {
if _, err := backend.DeleteObject("test", "foo"); err != nil {
t.Fatal(err)
}

View File

@@ -425,19 +425,19 @@ func (db *MultiBucketBackend) PutObject(bucketName, objectName string, meta map[
return nil
}
func (db *MultiBucketBackend) DeleteObject(bucketName, objectName string) error {
func (db *MultiBucketBackend) DeleteObject(bucketName, objectName string) (result gofakes3.ObjectDeleteResult, rerr error) {
db.lock.Lock()
defer db.lock.Unlock()
// Another slighly racy check:
exists, err := afero.Exists(db.bucketFs, bucketName)
if err != nil {
return err
return result, err
} else if !exists {
return gofakes3.BucketNotFound(bucketName)
return result, gofakes3.BucketNotFound(bucketName)
}
return db.deleteObjectLocked(bucketName, objectName)
return result, db.deleteObjectLocked(bucketName, objectName)
}
func (db *MultiBucketBackend) deleteObjectLocked(bucketName, objectName string) error {

View File

@@ -358,15 +358,15 @@ func (db *SingleBucketBackend) DeleteMulti(bucketName string, objects ...string)
return result, nil
}
func (db *SingleBucketBackend) DeleteObject(bucketName, objectName string) error {
func (db *SingleBucketBackend) DeleteObject(bucketName, objectName string) (result gofakes3.ObjectDeleteResult, rerr error) {
if bucketName != db.name {
return gofakes3.BucketNotFound(bucketName)
return result, gofakes3.BucketNotFound(bucketName)
}
db.lock.Lock()
defer db.lock.Unlock()
return db.deleteObjectLocked(bucketName, objectName)
return result, db.deleteObjectLocked(bucketName, objectName)
}
func (db *SingleBucketBackend) deleteObjectLocked(bucketName, objectName string) error {

View File

@@ -309,8 +309,8 @@ func (db *Backend) PutObject(bucketName, objectName string, meta map[string]stri
})
}
func (db *Backend) DeleteObject(bucketName, objectName string) error {
return db.bolt.Update(func(tx *bolt.Tx) error {
func (db *Backend) DeleteObject(bucketName, objectName string) (result gofakes3.ObjectDeleteResult, rerr error) {
return result, db.bolt.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte(bucketName))
if b == nil {
return gofakes3.BucketNotFound(bucketName)

View File

@@ -207,19 +207,19 @@ func (db *Backend) PutObject(bucketName, objectName string, meta map[string]stri
return nil
}
func (db *Backend) DeleteObject(bucketName, objectName string) error {
func (db *Backend) DeleteObject(bucketName, objectName string) (result gofakes3.ObjectDeleteResult, rerr error) {
db.lock.Lock()
defer db.lock.Unlock()
bucket := db.buckets[bucketName]
if bucket == nil {
return gofakes3.BucketNotFound(bucketName)
return result, gofakes3.BucketNotFound(bucketName)
}
// S3 does not report an error when attemping to delete a key that does not exist:
delete(bucket.data, objectName)
return nil
return result, nil
}
func (db *Backend) DeleteMulti(bucketName string, objects ...string) (result gofakes3.DeleteResult, err error) {

View File

@@ -454,14 +454,45 @@ func (g *GoFakeS3) createObject(bucket, object string, w http.ResponseWriter, r
return nil
}
// deleteObject deletes a S3 object from the bucket.
func (g *GoFakeS3) deleteObject(bucket, object string, w http.ResponseWriter, r *http.Request) error {
g.log.Print(LogInfo, "DELETE:", bucket, object)
if err := g.storage.DeleteObject(bucket, object); err != nil {
result, err := g.storage.DeleteObject(bucket, object)
if err != nil {
return err
}
w.Header().Set("x-amz-delete-marker", "false")
w.Write([]byte{})
if result.IsDeleteMarker {
w.Header().Set("x-amz-delete-marker", "true")
} else {
w.Header().Set("x-amz-delete-marker", "false")
}
if result.VersionID != "" {
w.Header().Set("x-amz-version-id", string(result.VersionID))
}
return nil
}
func (g *GoFakeS3) deleteObjectVersion(bucket, object string, version VersionID, w http.ResponseWriter, r *http.Request) error {
if g.versioned == nil {
return ErrNotImplemented
}
result, err := g.versioned.DeleteObjectVersion(bucket, object, version)
if err != nil {
return err
}
if result.IsDeleteMarker {
w.Header().Set("x-amz-delete-marker", "true")
} else {
w.Header().Set("x-amz-delete-marker", "false")
}
if result.VersionID != "" {
w.Header().Set("x-amz-version-id", string(result.VersionID))
}
return nil
}

View File

@@ -159,6 +159,8 @@ func (g *GoFakeS3) routeVersion(bucket, object string, versionID VersionID, w ht
return g.getObject(bucket, object, versionID, w, r)
case "HEAD":
return g.headObject(bucket, object, versionID, w, r)
case "DELETE":
return g.deleteObjectVersion(bucket, object, versionID, w, r)
default:
return ErrMethodNotAllowed
}