mirror of
https://github.com/minio/minio.git
synced 2024-12-24 06:05:55 -05:00
web: Simplify and converge common functions in web/obj API. (#4179)
RemoveObject() in webAPI currently re-implements some part of the code to remove objects combine them for simplicity and code convergence.
This commit is contained in:
parent
cf1fc45142
commit
57c5c75611
@ -17,6 +17,7 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@ -224,3 +225,36 @@ func canonicalizeETag(etag string) string {
|
|||||||
func isETagEqual(left, right string) bool {
|
func isETagEqual(left, right string) bool {
|
||||||
return canonicalizeETag(left) == canonicalizeETag(right)
|
return canonicalizeETag(left) == canonicalizeETag(right)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// deleteObject is a convenient wrapper to delete an object, this
|
||||||
|
// is a common function to be called from object handlers and
|
||||||
|
// web handlers.
|
||||||
|
func deleteObject(obj ObjectLayer, bucket, object string, r *http.Request) (err error) {
|
||||||
|
// Acquire a write lock before deleting the object.
|
||||||
|
objectLock := globalNSMutex.NewNSLock(bucket, object)
|
||||||
|
objectLock.Lock()
|
||||||
|
defer objectLock.Unlock()
|
||||||
|
|
||||||
|
// Proceed to delete the object.
|
||||||
|
if err = obj.DeleteObject(bucket, object); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get host and port from Request.RemoteAddr.
|
||||||
|
host, port, _ := net.SplitHostPort(r.RemoteAddr)
|
||||||
|
|
||||||
|
// Notify object deleted event.
|
||||||
|
eventNotify(eventData{
|
||||||
|
Type: ObjectRemovedDelete,
|
||||||
|
Bucket: bucket,
|
||||||
|
ObjInfo: ObjectInfo{
|
||||||
|
Name: object,
|
||||||
|
},
|
||||||
|
ReqParams: extractReqParams(r),
|
||||||
|
UserAgent: r.UserAgent(),
|
||||||
|
Host: host,
|
||||||
|
Port: port,
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -1016,35 +1016,12 @@ func (api objectAPIHandlers) DeleteObjectHandler(w http.ResponseWriter, r *http.
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
objectLock := globalNSMutex.NewNSLock(bucket, object)
|
// http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectDELETE.html
|
||||||
objectLock.Lock()
|
// Ignore delete object errors while replying to client, since we are
|
||||||
defer objectLock.Unlock()
|
// suppposed to reply only 204. Additionally log the error for
|
||||||
|
// investigation.
|
||||||
/// http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectDELETE.html
|
if err := deleteObject(objectAPI, bucket, object, r); err != nil {
|
||||||
/// Ignore delete object errors, since we are suppposed to reply
|
errorIf(err, "Unable to delete an object %s", pathJoin(bucket, object))
|
||||||
/// only 204.
|
|
||||||
if err := objectAPI.DeleteObject(bucket, object); err != nil {
|
|
||||||
writeSuccessNoContent(w)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
writeSuccessNoContent(w)
|
writeSuccessNoContent(w)
|
||||||
|
|
||||||
// Get host and port from Request.RemoteAddr.
|
|
||||||
host, port, err := net.SplitHostPort(r.RemoteAddr)
|
|
||||||
if err != nil {
|
|
||||||
host, port = "", ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify object deleted event.
|
|
||||||
eventNotify(eventData{
|
|
||||||
Type: ObjectRemovedDelete,
|
|
||||||
Bucket: bucket,
|
|
||||||
ObjInfo: ObjectInfo{
|
|
||||||
Name: object,
|
|
||||||
},
|
|
||||||
ReqParams: extractReqParams(r),
|
|
||||||
UserAgent: r.UserAgent(),
|
|
||||||
Host: host,
|
|
||||||
Port: port,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
@ -255,16 +255,22 @@ func (web *webAPIHandlers) ListObjects(r *http.Request, args *ListObjectsArgs, r
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveObjectArgs - args to remove an object
|
// RemoveObjectArgs - args to remove an object, JSON will look like.
|
||||||
// JSON will look like:
|
//
|
||||||
// '{"bucketname":"testbucket","objects":["photos/hawaii/","photos/maldives/","photos/sanjose.jpg"]}'
|
// {
|
||||||
|
// "bucketname": "testbucket",
|
||||||
|
// "objects": [
|
||||||
|
// "photos/hawaii/",
|
||||||
|
// "photos/maldives/",
|
||||||
|
// "photos/sanjose.jpg"
|
||||||
|
// ]
|
||||||
|
// }
|
||||||
type RemoveObjectArgs struct {
|
type RemoveObjectArgs struct {
|
||||||
Objects []string `json:"objects"` // can be files or sub-directories
|
Objects []string `json:"objects"` // Contains objects, prefixes.
|
||||||
Prefix string `json:"prefix"` // current directory in the browser-ui
|
BucketName string `json:"bucketname"` // Contains bucket name.
|
||||||
BucketName string `json:"bucketname"` // bucket name.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RemoveObject - removes an object.
|
// RemoveObject - removes an object, or all the objects at a given prefix.
|
||||||
func (web *webAPIHandlers) RemoveObject(r *http.Request, args *RemoveObjectArgs, reply *WebGenericRep) error {
|
func (web *webAPIHandlers) RemoveObject(r *http.Request, args *RemoveObjectArgs, reply *WebGenericRep) error {
|
||||||
objectAPI := web.ObjectAPI()
|
objectAPI := web.ObjectAPI()
|
||||||
if objectAPI == nil {
|
if objectAPI == nil {
|
||||||
@ -273,51 +279,35 @@ 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 {
|
if args.BucketName == "" || len(args.Objects) == 0 {
|
||||||
return toJSONError(errUnexpected)
|
return toJSONError(errInvalidArgument)
|
||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
objectLoop:
|
next:
|
||||||
for _, object := range args.Objects {
|
for _, objectName := range args.Objects {
|
||||||
remove := func(objectName string) error {
|
// If not a directory, remove the object.
|
||||||
objectLock := globalNSMutex.NewNSLock(args.BucketName, objectName)
|
if !hasSuffix(objectName, slashSeparator) && objectName != "" {
|
||||||
objectLock.Lock()
|
if err = deleteObject(objectAPI, args.BucketName, objectName, r); err != nil {
|
||||||
defer objectLock.Unlock()
|
break next
|
||||||
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: extractReqParams(r),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if !hasSuffix(object, slashSeparator) {
|
|
||||||
// If not a directory, remove the object.
|
|
||||||
err = remove(object)
|
|
||||||
if err != nil {
|
|
||||||
break objectLoop
|
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// For directories, list the contents recursively and remove.
|
// For directories, list the contents recursively and remove.
|
||||||
marker := ""
|
marker := ""
|
||||||
for {
|
for {
|
||||||
var lo ListObjectsInfo
|
var lo ListObjectsInfo
|
||||||
lo, err = objectAPI.ListObjects(args.BucketName, object, marker, "", 1000)
|
lo, err = objectAPI.ListObjects(args.BucketName, objectName, marker, "", 1000)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break objectLoop
|
break next
|
||||||
}
|
}
|
||||||
marker = lo.NextMarker
|
marker = lo.NextMarker
|
||||||
for _, obj := range lo.Objects {
|
for _, obj := range lo.Objects {
|
||||||
err = remove(obj.Name)
|
err = deleteObject(objectAPI, args.BucketName, obj.Name, r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break objectLoop
|
break next
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !lo.IsTruncated {
|
if !lo.IsTruncated {
|
||||||
@ -972,6 +962,12 @@ func toWebAPIError(err error) APIError {
|
|||||||
HTTPStatusCode: http.StatusForbidden,
|
HTTPStatusCode: http.StatusForbidden,
|
||||||
Description: err.Error(),
|
Description: err.Error(),
|
||||||
}
|
}
|
||||||
|
} else if err == errInvalidArgument {
|
||||||
|
return APIError{
|
||||||
|
Code: "InvalidArgument",
|
||||||
|
HTTPStatusCode: http.StatusBadRequest,
|
||||||
|
Description: err.Error(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Convert error type to api error code.
|
// Convert error type to api error code.
|
||||||
var apiErrCode APIErrorCode
|
var apiErrCode APIErrorCode
|
||||||
|
@ -439,7 +439,7 @@ func TestWebHandlerRemoveObject(t *testing.T) {
|
|||||||
ExecObjectLayerTest(t, testRemoveObjectWebHandler)
|
ExecObjectLayerTest(t, testRemoveObjectWebHandler)
|
||||||
}
|
}
|
||||||
|
|
||||||
// testRemoveObjectWebHandler - Test RemoveObject web handler
|
// testRemoveObjectWebHandler - Test RemoveObjectObject web handler
|
||||||
func testRemoveObjectWebHandler(obj ObjectLayer, instanceType string, t TestErrHandler) {
|
func testRemoveObjectWebHandler(obj ObjectLayer, instanceType string, t TestErrHandler) {
|
||||||
// Register the API end points with XL/FS object layer.
|
// Register the API end points with XL/FS object layer.
|
||||||
apiRouter := initTestWebRPCEndPoint(obj)
|
apiRouter := initTestWebRPCEndPoint(obj)
|
||||||
@ -477,9 +477,9 @@ 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, Objects: []string{"a/", "object"}}
|
removeRequest := RemoveObjectArgs{BucketName: bucketName, Objects: []string{"a/", "object"}}
|
||||||
removeObjectReply := &WebGenericRep{}
|
removeReply := &WebGenericRep{}
|
||||||
req, err := newTestWebRPCRequest("Web.RemoveObject", authorization, removeObjectRequest)
|
req, err := newTestWebRPCRequest("Web.RemoveObject", authorization, removeRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create HTTP request: <ERROR> %v", err)
|
t.Fatalf("Failed to create HTTP request: <ERROR> %v", err)
|
||||||
}
|
}
|
||||||
@ -487,14 +487,14 @@ func testRemoveObjectWebHandler(obj ObjectLayer, instanceType string, t TestErrH
|
|||||||
if rec.Code != http.StatusOK {
|
if rec.Code != http.StatusOK {
|
||||||
t.Fatalf("Expected the response status to be 200, but instead found `%d`", rec.Code)
|
t.Fatalf("Expected the response status to be 200, but instead found `%d`", rec.Code)
|
||||||
}
|
}
|
||||||
err = getTestWebRPCResponse(rec, &removeObjectReply)
|
err = getTestWebRPCResponse(rec, &removeReply)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed, %v", err)
|
t.Fatalf("Failed, %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
removeObjectRequest = RemoveObjectArgs{BucketName: bucketName, Objects: []string{"a/", "object"}}
|
removeRequest = RemoveObjectArgs{BucketName: bucketName, Objects: []string{"a/", "object"}}
|
||||||
removeObjectReply = &WebGenericRep{}
|
removeReply = &WebGenericRep{}
|
||||||
req, err = newTestWebRPCRequest("Web.RemoveObject", authorization, removeObjectRequest)
|
req, err = newTestWebRPCRequest("Web.RemoveObject", authorization, removeRequest)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create HTTP request: <ERROR> %v", err)
|
t.Fatalf("Failed to create HTTP request: <ERROR> %v", err)
|
||||||
}
|
}
|
||||||
@ -502,10 +502,28 @@ func testRemoveObjectWebHandler(obj ObjectLayer, instanceType string, t TestErrH
|
|||||||
if rec.Code != http.StatusOK {
|
if rec.Code != http.StatusOK {
|
||||||
t.Fatalf("Expected the response status to be 200, but instead found `%d`", rec.Code)
|
t.Fatalf("Expected the response status to be 200, but instead found `%d`", rec.Code)
|
||||||
}
|
}
|
||||||
err = getTestWebRPCResponse(rec, &removeObjectReply)
|
err = getTestWebRPCResponse(rec, &removeReply)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed, %v", err)
|
t.Fatalf("Failed, %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeRequest = RemoveObjectArgs{BucketName: bucketName}
|
||||||
|
removeReply = &WebGenericRep{}
|
||||||
|
req, err = newTestWebRPCRequest("Web.RemoveObject", authorization, removeRequest)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Failed to create HTTP request: <ERROR> %v", err)
|
||||||
|
}
|
||||||
|
apiRouter.ServeHTTP(rec, req)
|
||||||
|
if rec.Code != http.StatusOK {
|
||||||
|
t.Fatalf("Expected the response status to be 200, but instead found `%d`", rec.Code)
|
||||||
|
}
|
||||||
|
b, err := ioutil.ReadAll(rec.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !bytes.Contains(b, []byte("Invalid arguments specified")) {
|
||||||
|
t.Fatalf("Expected response wrong %s", string(b))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wrapper for calling Generate Auth Handler
|
// Wrapper for calling Generate Auth Handler
|
||||||
|
Loading…
Reference in New Issue
Block a user