More tests for web handlers (#2755)

* Return negative values of Total and Free in StorageInfo() when we fail to get disk info

* Return consistent messages in web handlers when the server is not initialized
This commit is contained in:
Anis Elleuch 2016-09-23 00:35:12 +01:00 committed by Harshavardhana
parent 79e0c52fc2
commit fc783f8407
3 changed files with 309 additions and 13 deletions

View File

@ -127,7 +127,7 @@ func (web *webAPIHandlers) StorageInfo(r *http.Request, args *GenericArgs, reply
reply.UIVersion = miniobrowser.UIVersion reply.UIVersion = miniobrowser.UIVersion
objectAPI := web.ObjectAPI() objectAPI := web.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
return &json2.Error{Message: "Volume not found"} return &json2.Error{Message: "Server not initialized"}
} }
reply.StorageInfo = objectAPI.StorageInfo() reply.StorageInfo = objectAPI.StorageInfo()
return nil return nil
@ -146,7 +146,7 @@ func (web *webAPIHandlers) MakeBucket(r *http.Request, args *MakeBucketArgs, rep
reply.UIVersion = miniobrowser.UIVersion reply.UIVersion = miniobrowser.UIVersion
objectAPI := web.ObjectAPI() objectAPI := web.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
return &json2.Error{Message: "Volume not found"} return &json2.Error{Message: "Server not initialized"}
} }
if err := objectAPI.MakeBucket(args.BucketName); err != nil { if err := objectAPI.MakeBucket(args.BucketName); err != nil {
return &json2.Error{Message: err.Error()} return &json2.Error{Message: err.Error()}
@ -175,7 +175,7 @@ func (web *webAPIHandlers) ListBuckets(r *http.Request, args *WebGenericArgs, re
} }
objectAPI := web.ObjectAPI() objectAPI := web.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
return &json2.Error{Message: "Volume not found"} return &json2.Error{Message: "Server not initialized"}
} }
buckets, err := objectAPI.ListBuckets() buckets, err := objectAPI.ListBuckets()
if err != nil { if err != nil {
@ -227,7 +227,7 @@ func (web *webAPIHandlers) ListObjects(r *http.Request, args *ListObjectsArgs, r
for { for {
objectAPI := web.ObjectAPI() objectAPI := web.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
return &json2.Error{Message: "Volume not found"} return &json2.Error{Message: "Server not initialized"}
} }
lo, err := objectAPI.ListObjects(args.BucketName, args.Prefix, marker, "/", 1000) lo, err := objectAPI.ListObjects(args.BucketName, args.Prefix, marker, "/", 1000)
if err != nil { if err != nil {
@ -269,7 +269,7 @@ func (web *webAPIHandlers) RemoveObject(r *http.Request, args *RemoveObjectArgs,
reply.UIVersion = miniobrowser.UIVersion reply.UIVersion = miniobrowser.UIVersion
objectAPI := web.ObjectAPI() objectAPI := web.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
return &json2.Error{Message: "Volume not found"} return &json2.Error{Message: "Server not initialized"}
} }
if err := objectAPI.DeleteObject(args.BucketName, args.ObjectName); err != nil { if err := objectAPI.DeleteObject(args.BucketName, args.ObjectName); err != nil {
return &json2.Error{Message: err.Error()} return &json2.Error{Message: err.Error()}
@ -412,7 +412,7 @@ func (web *webAPIHandlers) Upload(w http.ResponseWriter, r *http.Request) {
objectAPI := web.ObjectAPI() objectAPI := web.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeWebErrorResponse(w, errors.New("Volume not found")) writeWebErrorResponse(w, errors.New("Server not initialized"))
return return
} }
if _, err := objectAPI.PutObject(bucket, object, -1, r.Body, metadata); err != nil { if _, err := objectAPI.PutObject(bucket, object, -1, r.Body, metadata); err != nil {
@ -468,7 +468,7 @@ func (web *webAPIHandlers) Download(w http.ResponseWriter, r *http.Request) {
objectAPI := web.ObjectAPI() objectAPI := web.ObjectAPI()
if objectAPI == nil { if objectAPI == nil {
writeWebErrorResponse(w, errors.New("Volume not found")) writeWebErrorResponse(w, errors.New("Server not initialized"))
return return
} }
objInfo, err := objectAPI.GetObjectInfo(bucket, object) objInfo, err := objectAPI.GetObjectInfo(bucket, object)
@ -594,16 +594,16 @@ func (web *webAPIHandlers) SetBucketPolicy(r *http.Request, args *SetBucketPolic
if !isJWTReqAuthenticated(r) { if !isJWTReqAuthenticated(r) {
return &json2.Error{Message: "Unauthorized request"} return &json2.Error{Message: "Unauthorized request"}
} }
objectAPI := web.ObjectAPI()
if objectAPI == nil {
return &json2.Error{Message: "Server not initialized"}
}
bucketPolicy := policy.BucketPolicy(args.Policy) bucketPolicy := policy.BucketPolicy(args.Policy)
if !bucketPolicy.IsValidBucketPolicy() { if !bucketPolicy.IsValidBucketPolicy() {
return &json2.Error{Message: "Invalid policy " + args.Policy} return &json2.Error{Message: "Invalid policy " + args.Policy}
} }
objectAPI := web.ObjectAPI()
if objectAPI == nil {
return &json2.Error{Message: "Server not initialized"}
}
policyInfo, err := readBucketAccessPolicy(objectAPI, args.BucketName) policyInfo, err := readBucketAccessPolicy(objectAPI, args.BucketName)
if err != nil { if err != nil {
return &json2.Error{Message: err.Error()} return &json2.Error{Message: err.Error()}

View File

@ -23,8 +23,10 @@ import (
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"strconv" "strconv"
"strings"
"testing" "testing"
router "github.com/gorilla/mux"
"github.com/minio/minio-go/pkg/policy" "github.com/minio/minio-go/pkg/policy"
) )
@ -742,7 +744,12 @@ func testWebGetBucketPolicyHandler(obj ObjectLayer, instanceType string, t TestE
rec := httptest.NewRecorder() rec := httptest.NewRecorder()
bucketName := getRandomBucketName() bucketName := getRandomBucketName()
if err = obj.MakeBucket(bucketName); err != nil { if err := obj.MakeBucket(bucketName); err != nil {
t.Fatal("Unexpected error: ", err)
}
policyDoc := "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Action\":[\"s3:GetBucketLocation\",\"s3:ListBucket\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Resource\":[\"arn:aws:s3:::" + bucketName + "\"],\"Sid\":\"\"},{\"Action\":[\"s3:GetObject\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Resource\":[\"arn:aws:s3:::" + bucketName + "/*\"],\"Sid\":\"\"}]}"
if err := writeBucketPolicy(bucketName, obj, bytes.NewReader([]byte(policyDoc)), int64(len(policyDoc))); err != nil {
t.Fatal("Unexpected error: ", err) t.Fatal("Unexpected error: ", err)
} }
@ -751,7 +758,7 @@ func testWebGetBucketPolicyHandler(obj ObjectLayer, instanceType string, t TestE
prefix string prefix string
expectedResult policy.BucketPolicy expectedResult policy.BucketPolicy
}{ }{
{bucketName, "", policy.BucketPolicyNone}, {bucketName, "", policy.BucketPolicyReadOnly},
} }
for i, testCase := range testCases { for i, testCase := range testCases {
@ -846,3 +853,286 @@ func testWebSetBucketPolicyHandler(obj ObjectLayer, instanceType string, t TestE
} }
} }
} }
// TestWebCheckAuthorization - Test Authorization for all web handlers
func TestWebCheckAuthorization(t *testing.T) {
// Prepare XL backend
obj, fsDirs, e := prepareXL()
if e != nil {
t.Fatalf("Initialization of object layer failed for XL setup: %s", e)
}
// Executing the object layer tests for XL.
defer removeRoots(fsDirs)
// Register the API end points with XL/FS object layer.
apiRouter := initTestWebRPCEndPoint(obj)
// initialize the server and obtain the credentials and root.
// credentials are necessary to sign the HTTP request.
rootPath, e := newTestConfig("us-east-1")
if e != nil {
t.Fatalf("Init Test config failed")
}
// remove the root folder after the test ends.
defer removeAll(rootPath)
rec := httptest.NewRecorder()
// Check if web rpc calls return unauthorized request with an incorrect token
webRPCs := []string{"ServerInfo", "StorageInfo", "MakeBucket", "ListBuckets", "ListObjects", "RemoveObject", "GenerateAuth",
"SetAuth", "GetAuth", "GetBucketPolicy", "SetBucketPolicy"}
for _, rpcCall := range webRPCs {
args := &GenericArgs{}
reply := &WebGenericRep{}
req, err := newTestWebRPCRequest("Web."+rpcCall, "Bearer fooauthorization", args)
if err != nil {
t.Fatalf("Test %s: Failed to create HTTP request: <ERROR> %v", rpcCall, err)
}
apiRouter.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("Test %s: Expected the response status to be 200, but instead found `%d`", rpcCall, rec.Code)
}
err = getTestWebRPCResponse(rec, &reply)
if err == nil {
t.Fatalf("Test %s: Should fail", rpcCall)
} else {
if !strings.Contains(err.Error(), "Unauthorized request") {
t.Fatalf("Test %s: should fail with Unauthorized request. Found error: %v", rpcCall, err)
}
}
}
// Test authorization of Web.Download
req, err := http.NewRequest("GET", "/minio/download/bucket/object?token=wrongauth", nil)
if err != nil {
t.Fatalf("Cannot create upload request, %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)
}
resp := string(rec.Body.Bytes())
if !strings.Contains(resp, "Invalid token") {
t.Fatalf("Unexpected error message, expected: `Invalid token`, found: `%s`", resp)
}
// Test authorization of Web.Upload
content := []byte("temporary file's content")
req, err = http.NewRequest("PUT", "/minio/upload/bucket/object", nil)
req.Header.Set("Authorization", "Bearer foo-authorization")
req.Header.Set("Content-Length", strconv.Itoa(len(content)))
req.Header.Set("x-amz-date", "20160814T114029Z")
req.Header.Set("Accept", "*/*")
req.Body = ioutil.NopCloser(bytes.NewReader(content))
if err != nil {
t.Fatalf("Cannot create upload request, %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)
}
resp = string(rec.Body.Bytes())
if !strings.Contains(resp, "Invalid token") {
t.Fatalf("Unexpected error message, expected: `Invalid token`, found: `%s`", resp)
}
}
// TestWebObjectLayerNotReady - Test RPCs responses when disks are not ready
func TestWebObjectLayerNotReady(t *testing.T) {
webHandlers := &webAPIHandlers{
ObjectAPI: func() ObjectLayer { return nil },
}
// Initialize router.
apiRouter := router.NewRouter()
registerWebRouter(apiRouter, webHandlers)
// initialize the server and obtain the credentials and root.
// credentials are necessary to sign the HTTP request.
rootPath, e := newTestConfig("us-east-1")
if e != nil {
t.Fatalf("Init Test config failed")
}
// remove the root folder after the test ends.
defer removeAll(rootPath)
rec := httptest.NewRecorder()
credentials := serverConfig.GetCredential()
authorization, e := getWebRPCToken(apiRouter, credentials.AccessKeyID, credentials.SecretAccessKey)
if e != nil {
t.Fatal("Cannot authenticate")
}
// Check if web rpc calls return Server not initialized. ServerInfo, GenerateAuth,
// SetAuth and GetAuth are not concerned
webRPCs := []string{"StorageInfo", "MakeBucket", "ListBuckets", "ListObjects", "RemoveObject",
"GetBucketPolicy", "SetBucketPolicy"}
for _, rpcCall := range webRPCs {
args := &GenericArgs{}
reply := &WebGenericRep{}
req, err := newTestWebRPCRequest("Web."+rpcCall, authorization, args)
if err != nil {
t.Fatalf("Test %s: Failed to create HTTP request: <ERROR> %v", rpcCall, err)
}
apiRouter.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("Test %s: Expected the response status to be 200, but instead found `%d`", rpcCall, rec.Code)
}
err = getTestWebRPCResponse(rec, &reply)
if err == nil {
t.Fatalf("Test %s: Should fail", rpcCall)
} else {
if !strings.Contains(err.Error(), "Server not initialized") {
t.Fatalf("Test %s: should fail with Unauthorized request. Found error: %v", rpcCall, err)
}
}
}
// Test authorization of Web.Download
req, err := http.NewRequest("GET", "/minio/download/bucket/object?token="+authorization, nil)
if err != nil {
t.Fatalf("Cannot create upload request, %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)
}
resp := string(rec.Body.Bytes())
if !strings.Contains(resp, "We encountered an internal error, please try again.") {
t.Fatalf("Unexpected error message, expected: `Invalid token`, found: `%s`", resp)
}
// Test authorization of Web.Upload
content := []byte("temporary file's content")
req, err = http.NewRequest("PUT", "/minio/upload/bucket/object", nil)
req.Header.Set("Authorization", "Bearer "+authorization)
req.Header.Set("Content-Length", strconv.Itoa(len(content)))
req.Header.Set("x-amz-date", "20160814T114029Z")
req.Header.Set("Accept", "*/*")
req.Body = ioutil.NopCloser(bytes.NewReader(content))
if err != nil {
t.Fatalf("Cannot create upload request, %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)
}
resp = string(rec.Body.Bytes())
if !strings.Contains(resp, "We encountered an internal error, please try again.") {
t.Fatalf("Unexpected error message, expected: `Invalid token`, found: `%s`", resp)
}
}
// TestWebObjectLayerFaultyDisks - Test Web RPC responses with faulty disks
func TestWebObjectLayerFaultyDisks(t *testing.T) {
// Prepare XL backend
obj, fsDirs, e := prepareXL()
if e != nil {
t.Fatalf("Initialization of object layer failed for XL setup: %s", e)
}
// Executing the object layer tests for XL.
defer removeRoots(fsDirs)
// Set faulty disks to XL backend
xl := obj.(xlObjects)
for i, d := range xl.storageDisks {
xl.storageDisks[i] = newNaughtyDisk(d.(*posix), nil, errFaultyDisk)
}
webHandlers := &webAPIHandlers{
ObjectAPI: func() ObjectLayer { return obj },
}
// Initialize router.
apiRouter := router.NewRouter()
registerWebRouter(apiRouter, webHandlers)
// initialize the server and obtain the credentials and root.
// credentials are necessary to sign the HTTP request.
rootPath, e := newTestConfig("us-east-1")
if e != nil {
t.Fatalf("Init Test config failed")
}
// remove the root folder after the test ends.
defer removeAll(rootPath)
rec := httptest.NewRecorder()
credentials := serverConfig.GetCredential()
authorization, e := getWebRPCToken(apiRouter, credentials.AccessKeyID, credentials.SecretAccessKey)
if e != nil {
t.Fatal("Cannot authenticate")
}
// Check if web rpc calls return errors with faulty disks. ServerInfo, GenerateAuth, SetAuth, GetAuth are not concerned
webRPCs := []string{"MakeBucket", "ListBuckets", "ListObjects", "RemoveObject",
"GetBucketPolicy", "SetBucketPolicy"}
for _, rpcCall := range webRPCs {
args := &GenericArgs{}
reply := &WebGenericRep{}
req, err := newTestWebRPCRequest("Web."+rpcCall, authorization, args)
if err != nil {
t.Fatalf("Test %s: Failed to create HTTP request: <ERROR> %v", rpcCall, err)
}
apiRouter.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("Test %s: Expected the response status to be 200, but instead found `%d`", rpcCall, rec.Code)
}
err = getTestWebRPCResponse(rec, &reply)
if err == nil {
t.Errorf("Test %s: Should fail", rpcCall)
}
}
// Test Web.StorageInfo
storageInfoRequest := GenericArgs{}
storageInfoReply := &StorageInfoRep{}
req, err := newTestWebRPCRequest("Web.StorageInfo", authorization, storageInfoRequest)
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)
}
err = getTestWebRPCResponse(rec, &storageInfoReply)
if err != nil {
t.Fatalf("Failed %v", err)
}
if storageInfoReply.StorageInfo.Total != -1 || storageInfoReply.StorageInfo.Free != -1 {
t.Fatalf("Should get negative values of Total and Free since disks are faulty ")
}
// Test authorization of Web.Download
req, err = http.NewRequest("GET", "/minio/download/bucket/object?token="+authorization, nil)
if err != nil {
t.Fatalf("Cannot create upload request, %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)
}
resp := string(rec.Body.Bytes())
if !strings.Contains(resp, "We encountered an internal error, please try again.") {
t.Fatalf("Unexpected error message, expected: `Invalid token`, found: `%s`", resp)
}
// Test authorization of Web.Upload
content := []byte("temporary file's content")
req, err = http.NewRequest("PUT", "/minio/upload/bucket/object", nil)
req.Header.Set("Authorization", "Bearer "+authorization)
req.Header.Set("Content-Length", strconv.Itoa(len(content)))
req.Header.Set("x-amz-date", "20160814T114029Z")
req.Header.Set("Accept", "*/*")
req.Body = ioutil.NopCloser(bytes.NewReader(content))
if err != nil {
t.Fatalf("Cannot create upload request, %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)
}
resp = string(rec.Body.Bytes())
if !strings.Contains(resp, "We encountered an internal error, please try again.") {
t.Fatalf("Unexpected error message, expected: `Invalid token`, found: `%s`", resp)
}
}

View File

@ -224,6 +224,12 @@ func (xl xlObjects) StorageInfo() StorageInfo {
// Sort so that the first element is the smallest. // Sort so that the first element is the smallest.
sort.Sort(byDiskTotal(disksInfo)) sort.Sort(byDiskTotal(disksInfo))
if len(disksInfo) == 0 {
return StorageInfo{
Total: -1,
Free: -1,
}
}
// Return calculated storage info, choose the lowest Total and // Return calculated storage info, choose the lowest Total and
// Free as the total aggregated values. Total capacity is always // Free as the total aggregated values. Total capacity is always
// the multiple of smallest disk among the disk list. // the multiple of smallest disk among the disk list.