web-handlers: Support removal of multiple objects at once. (#3810)

This commit is contained in:
Krishna Srinivas 2017-02-28 19:07:28 -08:00 committed by Harshavardhana
parent 2b0ed21f08
commit 91cf54f895
3 changed files with 71 additions and 28 deletions

View File

@ -36,7 +36,7 @@ test_script:
# Unit tests # Unit tests
- ps: Add-AppveyorTest "Unit Tests" -Outcome Running - ps: Add-AppveyorTest "Unit Tests" -Outcome Running
- mkdir build\coverage - mkdir build\coverage
- go test -timeout 15m -race github.com/minio/minio/cmd... - go test -timeout 17m -race github.com/minio/minio/cmd...
- go test -race github.com/minio/minio/pkg... - go test -race github.com/minio/minio/pkg...
- go test -coverprofile=build\coverage\coverage.txt -covermode=atomic github.com/minio/minio/cmd - go test -coverprofile=build\coverage\coverage.txt -covermode=atomic github.com/minio/minio/cmd
- ps: Update-AppveyorTest "Unit Tests" -Outcome Passed - ps: Update-AppveyorTest "Unit Tests" -Outcome Passed

View File

@ -249,10 +249,12 @@ func (web *webAPIHandlers) ListObjects(r *http.Request, args *ListObjectsArgs, r
} }
// RemoveObjectArgs - args to remove an object // RemoveObjectArgs - args to remove an object
// JSON will look like:
// '{"bucketname":"testbucket","prefix":"john/pics/","objects":["hawaii/","maldives/","sanjose.jpg"]}'
type RemoveObjectArgs struct { type RemoveObjectArgs struct {
TargetHost string `json:"targetHost"` Objects []string `json:"objects"` // can be files or sub-directories
BucketName string `json:"bucketName"` Prefix string `json:"prefix"` // current directory in the browser-ui
ObjectName string `json:"objectName"` BucketName string `json:"bucketname"` // bucket name.
} }
// RemoveObject - removes an object. // RemoveObject - removes an object.
@ -264,31 +266,65 @@ func (web *webAPIHandlers) RemoveObject(r *http.Request, args *RemoveObjectArgs,
if !isHTTPRequestValid(r) { if !isHTTPRequestValid(r) {
return toJSONError(errAuthentication) return toJSONError(errAuthentication)
} }
if args.BucketName == "" || len(args.Objects) == 0 {
objectLock := globalNSMutex.NewNSLock(args.BucketName, args.ObjectName) return toJSONError(errUnexpected)
objectLock.Lock() }
defer objectLock.Unlock() var err error
objectLoop:
if err := objectAPI.DeleteObject(args.BucketName, args.ObjectName); err != nil { for _, object := range args.Objects {
if isErrObjectNotFound(err) { remove := func(objectName string) error {
// Ignore object not found error. objectLock := globalNSMutex.NewNSLock(args.BucketName, objectName)
reply.UIVersion = browser.UIVersion objectLock.Lock()
return nil defer objectLock.Unlock()
err = objectAPI.DeleteObject(args.BucketName, objectName)
if err == nil {
// Notify object deleted event.
eventNotify(eventData{
Type: ObjectRemovedDelete,
Bucket: args.BucketName,
ObjInfo: ObjectInfo{
Name: objectName,
},
ReqParams: map[string]string{
"sourceIPAddress": r.RemoteAddr,
},
})
}
return err
}
if !hasSuffix(object, slashSeparator) {
// If not a directory, compress the file and write it to response.
err = remove(pathJoin(args.Prefix, object))
if err != nil {
break objectLoop
}
continue
}
// For directories, list the contents recursively and remove.
marker := ""
for {
var lo ListObjectsInfo
lo, err = objectAPI.ListObjects(args.BucketName, pathJoin(args.Prefix, object), marker, "", 1000)
if err != nil {
break objectLoop
}
marker = lo.NextMarker
for _, obj := range lo.Objects {
err = remove(obj.Name)
if err != nil {
break objectLoop
}
}
if !lo.IsTruncated {
break
}
} }
return toJSONError(err, args.BucketName, args.ObjectName)
} }
// Notify object deleted event. if err != nil && !isErrObjectNotFound(err) {
eventNotify(eventData{ // Ignore object not found error.
Type: ObjectRemovedDelete, return toJSONError(err, args.BucketName, "")
Bucket: args.BucketName, }
ObjInfo: ObjectInfo{
Name: args.ObjectName,
},
ReqParams: map[string]string{
"sourceIPAddress": r.RemoteAddr,
},
})
reply.UIVersion = browser.UIVersion reply.UIVersion = browser.UIVersion
return nil return nil

View File

@ -468,7 +468,14 @@ func testRemoveObjectWebHandler(obj ObjectLayer, instanceType string, t TestErrH
t.Fatalf("Was not able to upload an object, %v", err) t.Fatalf("Was not able to upload an object, %v", err)
} }
removeObjectRequest := RemoveObjectArgs{BucketName: bucketName, ObjectName: objectName} objectName = "a/object"
_, err = obj.PutObject(bucketName, objectName, int64(len(data)), bytes.NewReader(data),
map[string]string{"md5Sum": "c9a34cfc85d982698c6ac89f76071abd"}, "")
if err != nil {
t.Fatalf("Was not able to upload an object, %v", err)
}
removeObjectRequest := RemoveObjectArgs{BucketName: bucketName, Prefix: "", Objects: []string{"a/", "object"}}
removeObjectReply := &WebGenericRep{} removeObjectReply := &WebGenericRep{}
req, err := newTestWebRPCRequest("Web.RemoveObject", authorization, removeObjectRequest) req, err := newTestWebRPCRequest("Web.RemoveObject", authorization, removeObjectRequest)
if err != nil { if err != nil {
@ -483,7 +490,7 @@ func testRemoveObjectWebHandler(obj ObjectLayer, instanceType string, t TestErrH
t.Fatalf("Failed, %v", err) t.Fatalf("Failed, %v", err)
} }
removeObjectRequest = RemoveObjectArgs{BucketName: bucketName, ObjectName: objectName} removeObjectRequest = RemoveObjectArgs{BucketName: bucketName, Prefix: "", Objects: []string{"a/", "object"}}
removeObjectReply = &WebGenericRep{} removeObjectReply = &WebGenericRep{}
req, err = newTestWebRPCRequest("Web.RemoveObject", authorization, removeObjectRequest) req, err = newTestWebRPCRequest("Web.RemoveObject", authorization, removeObjectRequest)
if err != nil { if err != nil {