mirror of
https://github.com/alist-org/gofakes3.git
synced 2025-12-24 12:58:04 +08:00
Delete object version endpoint
This commit is contained in:
23
backend.go
23
backend.go
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
39
gofakes3.go
39
gofakes3.go
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user