mirror of
https://github.com/minio/minio.git
synced 2024-12-25 14:45:54 -05:00
f903cae6ff
Current implementation requires server pools to have same erasure stripe sizes, to facilitate same SLA and expectations. This PR allows server pools to be variadic, i.e they do not have to be same erasure stripe sizes - instead they should have SLA for parity ratio. If the parity ratio cannot be guaranteed by the new server pool, the deployment is rejected i.e server pool expansion is not allowed.
614 lines
27 KiB
Go
614 lines
27 KiB
Go
/*
|
|
* MinIO Cloud Storage, (C) 2016, 2017 MinIO, Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"runtime"
|
|
"strings"
|
|
"testing"
|
|
|
|
humanize "github.com/dustin/go-humanize"
|
|
)
|
|
|
|
// Wrapper for calling GetObject tests for both Erasure multiple disks and single node setup.
|
|
func TestGetObject(t *testing.T) {
|
|
ExecExtendedObjectLayerTest(t, testGetObject)
|
|
}
|
|
|
|
// ObjectLayer.GetObject is called with series of cases for valid and erroneous inputs and the result is validated.
|
|
func testGetObject(obj ObjectLayer, instanceType string, t TestErrHandler) {
|
|
// Setup for the tests.
|
|
bucketName := getRandomBucketName()
|
|
objectName := "test-object"
|
|
emptyDirName := "test-empty-dir/"
|
|
|
|
// create bucket.
|
|
err := obj.MakeBucketWithLocation(context.Background(), bucketName, BucketOptions{})
|
|
// Stop the test if creation of the bucket fails.
|
|
if err != nil {
|
|
t.Fatalf("%s : %s", instanceType, err.Error())
|
|
}
|
|
|
|
// set of byte data for PutObject.
|
|
// object has to be created before running tests for GetObject.
|
|
// this is required even to assert the GetObject data,
|
|
// since dataInserted === dataFetched back is a primary criteria for any object storage this assertion is critical.
|
|
bytesData := []struct {
|
|
byteData []byte
|
|
}{
|
|
// Regular data
|
|
{generateBytesData(6 * humanize.MiByte)},
|
|
// Empty data for empty directory
|
|
{},
|
|
}
|
|
// set of inputs for uploading the objects before tests for downloading is done.
|
|
putObjectInputs := []struct {
|
|
bucketName string
|
|
objectName string
|
|
contentLength int64
|
|
textData []byte
|
|
metaData map[string]string
|
|
}{
|
|
// case - 1.
|
|
{bucketName, objectName, int64(len(bytesData[0].byteData)), bytesData[0].byteData, make(map[string]string)},
|
|
{bucketName, emptyDirName, int64(len(bytesData[1].byteData)), bytesData[1].byteData, make(map[string]string)},
|
|
}
|
|
// iterate through the above set of inputs and upkoad the object.
|
|
for i, input := range putObjectInputs {
|
|
// uploading the object.
|
|
_, err = obj.PutObject(context.Background(), input.bucketName, input.objectName, mustGetPutObjReader(t, bytes.NewBuffer(input.textData), input.contentLength, input.metaData["etag"], ""), ObjectOptions{UserDefined: input.metaData})
|
|
// if object upload fails stop the test.
|
|
if err != nil {
|
|
t.Fatalf("Put Object case %d: Error uploading object: <ERROR> %v", i+1, err)
|
|
}
|
|
}
|
|
// set of empty buffers used to fill GetObject data.
|
|
buffers := []*bytes.Buffer{
|
|
new(bytes.Buffer),
|
|
new(bytes.Buffer),
|
|
new(bytes.Buffer),
|
|
}
|
|
|
|
// test cases with set of inputs
|
|
testCases := []struct {
|
|
bucketName string
|
|
objectName string
|
|
startOffset int64
|
|
length int64
|
|
// data obtained/fetched from GetObject.
|
|
getObjectData *bytes.Buffer
|
|
// writer which governs the write into the `getObjectData`.
|
|
writer io.Writer
|
|
// flag indicating whether the test for given ase should pass.
|
|
shouldPass bool
|
|
// expected Result.
|
|
expectedData []byte
|
|
err error
|
|
}{
|
|
// Test case 1-4.
|
|
// Cases with invalid bucket names.
|
|
{".test", "obj", 0, 0, nil, nil, false, []byte(""), fmt.Errorf("%s", "Bucket name invalid: .test")},
|
|
{"------", "obj", 0, 0, nil, nil, false, []byte(""), fmt.Errorf("%s", "Bucket name invalid: ------")},
|
|
{"$this-is-not-valid-too", "obj", 0, 0, nil, nil, false,
|
|
[]byte(""), fmt.Errorf("%s", "Bucket name invalid: $this-is-not-valid-too")},
|
|
{"a", "obj", 0, 0, nil, nil, false, []byte(""), fmt.Errorf("%s", "Bucket name invalid: a")},
|
|
// Test case - 5.
|
|
// Case with invalid object names.
|
|
{bucketName, "", 0, 0, nil, nil, false, []byte(""), fmt.Errorf("%s", "Object name invalid: "+bucketName+"/")},
|
|
// Test case - 6.
|
|
{bucketName, objectName, 0, int64(len(bytesData[0].byteData)), buffers[0], NewEOFWriter(buffers[0], 100), false, []byte{}, io.EOF},
|
|
// Test case with start offset set to 0 and length set to size of the object.
|
|
// Fetching the entire object.
|
|
// Test case - 7.
|
|
{bucketName, objectName, 0, int64(len(bytesData[0].byteData)), buffers[1], buffers[1], true, bytesData[0].byteData, nil},
|
|
// Test case with `length` parameter set to a negative value.
|
|
// Test case - 8.
|
|
{bucketName, objectName, 0, int64(-1), buffers[1], buffers[1], true, bytesData[0].byteData, nil},
|
|
// Test case with content-range 1 to objectSize .
|
|
// Test case - 9.
|
|
{bucketName, objectName, 1, int64(len(bytesData[0].byteData) - 1), buffers[1], buffers[1], true, bytesData[0].byteData[1:], nil},
|
|
// Test case with content-range 100 to objectSize - 100.
|
|
// Test case - 10.
|
|
{bucketName, objectName, 100, int64(len(bytesData[0].byteData) - 200), buffers[1], buffers[1], true,
|
|
bytesData[0].byteData[100 : len(bytesData[0].byteData)-100], nil},
|
|
// Test case with offset greater than the size of the object
|
|
// Test case - 11.
|
|
{bucketName, objectName, int64(len(bytesData[0].byteData) + 1), int64(len(bytesData[0].byteData)), buffers[0],
|
|
NewEOFWriter(buffers[0], 100), false, []byte{},
|
|
InvalidRange{int64(len(bytesData[0].byteData) + 1), int64(len(bytesData[0].byteData)), int64(len(bytesData[0].byteData))}},
|
|
// Test case with offset greater than the size of the object.
|
|
// Test case - 12.
|
|
{bucketName, objectName, -1, int64(len(bytesData[0].byteData)), buffers[0], new(bytes.Buffer), false, []byte{}, errUnexpected},
|
|
// Test case length parameter is more than the object size.
|
|
// Test case - 13.
|
|
{bucketName, objectName, 0, int64(len(bytesData[0].byteData) + 1), buffers[1], buffers[1], false, bytesData[0].byteData,
|
|
InvalidRange{0, int64(len(bytesData[0].byteData) + 1), int64(len(bytesData[0].byteData))}},
|
|
// Test case with offset + length > objectSize parameter set to a negative value.
|
|
// Test case - 14.
|
|
{bucketName, objectName, 2, int64(len(bytesData[0].byteData)), buffers[1], buffers[1], false, bytesData[0].byteData,
|
|
InvalidRange{2, int64(len(bytesData[0].byteData)), int64(len(bytesData[0].byteData))}},
|
|
// Test case with the writer set to nil.
|
|
// Test case - 15.
|
|
{bucketName, objectName, 0, int64(len(bytesData[0].byteData)), buffers[1], nil, false, bytesData[0].byteData, errUnexpected},
|
|
// Test case - 16.
|
|
// Test case when it is an empty directory
|
|
{bucketName, emptyDirName, 0, int64(len(bytesData[1].byteData)), buffers[2], buffers[2], true, bytesData[1].byteData, nil},
|
|
}
|
|
|
|
for i, testCase := range testCases {
|
|
err = obj.GetObject(context.Background(), testCase.bucketName, testCase.objectName, testCase.startOffset, testCase.length, testCase.writer, "", ObjectOptions{})
|
|
if err != nil && testCase.shouldPass {
|
|
t.Errorf("Test %d: %s: Expected to pass, but failed with: <ERROR> %s", i+1, instanceType, err.Error())
|
|
}
|
|
if err == nil && !testCase.shouldPass {
|
|
t.Errorf("Test %d: %s: Expected to fail with <ERROR> \"%s\", but passed instead.", i+1, instanceType, testCase.err.Error())
|
|
}
|
|
// Failed as expected, but does it fail for the expected reason.
|
|
if err != nil && !testCase.shouldPass {
|
|
if !strings.Contains(err.Error(), testCase.err.Error()) {
|
|
t.Errorf("Test %d: %s: Expected to fail with error \"%s\", but instead failed with error \"%s\" instead.", i+1, instanceType, testCase.err.Error(), err.Error())
|
|
}
|
|
}
|
|
// Since there are cases for which GetObject fails, this is
|
|
// necessary. Test passes as expected, but the output values
|
|
// are verified for correctness here.
|
|
if err == nil && testCase.shouldPass {
|
|
if !bytes.Equal(testCase.expectedData, testCase.getObjectData.Bytes()) {
|
|
t.Errorf("Test %d: %s: Data Mismatch: Expected data and the fetched data from GetObject doesn't match.", i+1, instanceType)
|
|
}
|
|
// empty the buffer so that it can be used to further cases.
|
|
testCase.getObjectData.Reset()
|
|
}
|
|
}
|
|
}
|
|
|
|
// Wrapper for calling GetObject with permission denied expected
|
|
func TestGetObjectPermissionDenied(t *testing.T) {
|
|
// Windows doesn't support Chmod under golang
|
|
if runtime.GOOS != globalWindowsOSName {
|
|
ExecObjectLayerDiskAlteredTest(t, testGetObjectPermissionDenied)
|
|
}
|
|
}
|
|
|
|
// Test GetObject when we are allowed to access some dirs and objects
|
|
func testGetObjectPermissionDenied(obj ObjectLayer, instanceType string, disks []string, t *testing.T) {
|
|
// Setup for the tests.
|
|
bucketName := getRandomBucketName()
|
|
// create bucket.
|
|
err := obj.MakeBucketWithLocation(context.Background(), bucketName, BucketOptions{})
|
|
// Stop the test if creation of the bucket fails.
|
|
if err != nil {
|
|
t.Fatalf("%s : %s", instanceType, err.Error())
|
|
}
|
|
|
|
bytesData := []struct {
|
|
byteData []byte
|
|
}{
|
|
{generateBytesData(6 * humanize.MiByte)},
|
|
}
|
|
// set of inputs for uploading the objects before tests for downloading is done.
|
|
putObjectInputs := []struct {
|
|
bucketName string
|
|
objectName string
|
|
contentLength int64
|
|
textData []byte
|
|
metaData map[string]string
|
|
}{
|
|
{bucketName, "test-object1", int64(len(bytesData[0].byteData)), bytesData[0].byteData, make(map[string]string)},
|
|
{bucketName, "test-object2", int64(len(bytesData[0].byteData)), bytesData[0].byteData, make(map[string]string)},
|
|
{bucketName, "dir/test-object3", int64(len(bytesData[0].byteData)), bytesData[0].byteData, make(map[string]string)},
|
|
}
|
|
// iterate through the above set of inputs and upkoad the object.
|
|
for i, input := range putObjectInputs {
|
|
// uploading the object.
|
|
_, err = obj.PutObject(context.Background(), input.bucketName, input.objectName, mustGetPutObjReader(t, bytes.NewBuffer(input.textData), input.contentLength, input.metaData["etag"], ""), ObjectOptions{UserDefined: input.metaData})
|
|
// if object upload fails stop the test.
|
|
if err != nil {
|
|
t.Fatalf("Put Object case %d: Error uploading object: <ERROR> %v", i+1, err)
|
|
}
|
|
}
|
|
|
|
// set of empty buffers used to fill GetObject data.
|
|
buffers := []*bytes.Buffer{
|
|
new(bytes.Buffer),
|
|
}
|
|
|
|
// test cases with set of inputs
|
|
testCases := []struct {
|
|
bucketName string
|
|
objectName string
|
|
chmodPath string
|
|
startOffset int64
|
|
length int64
|
|
// data obtained/fetched from GetObject.
|
|
getObjectData *bytes.Buffer
|
|
// writer which governs the write into the `getObjectData`.
|
|
writer io.Writer
|
|
// flag indicating whether the test for given ase should pass.
|
|
shouldPass bool
|
|
// expected Result.
|
|
expectedData []byte
|
|
err error
|
|
}{
|
|
// Test 1 - chmod 000 bucket/test-object1
|
|
{bucketName, "test-object1", "test-object1", 0, int64(len(bytesData[0].byteData)), buffers[0], buffers[0], false, bytesData[0].byteData, PrefixAccessDenied{Bucket: bucketName, Object: "test-object1"}},
|
|
// Test 2 - chmod 000 bucket/dir/
|
|
{bucketName, "dir/test-object2", "dir", 0, int64(len(bytesData[0].byteData)), buffers[0], buffers[0], false, bytesData[0].byteData, PrefixAccessDenied{Bucket: bucketName, Object: "dir/test-object2"}},
|
|
// Test 3 - chmod 000 bucket/
|
|
{bucketName, "test-object3", "", 0, int64(len(bytesData[0].byteData)), buffers[0], buffers[0], false, bytesData[0].byteData, PrefixAccessDenied{Bucket: bucketName, Object: "test-object3"}},
|
|
}
|
|
|
|
for i, testCase := range testCases {
|
|
for _, d := range disks {
|
|
err = os.Chmod(d+SlashSeparator+testCase.bucketName+SlashSeparator+testCase.chmodPath, 0)
|
|
if err != nil {
|
|
t.Fatalf("Test %d, Unable to chmod: %v", i+1, err)
|
|
}
|
|
}
|
|
|
|
err = obj.GetObject(context.Background(), testCase.bucketName, testCase.objectName, testCase.startOffset, testCase.length, testCase.writer, "", ObjectOptions{})
|
|
if err != nil && testCase.shouldPass {
|
|
t.Errorf("Test %d: %s: Expected to pass, but failed with: <ERROR> %s", i+1, instanceType, err.Error())
|
|
}
|
|
if err == nil && !testCase.shouldPass {
|
|
t.Errorf("Test %d: %s: Expected to fail with <ERROR> \"%s\", but passed instead.", i+1, instanceType, testCase.err.Error())
|
|
}
|
|
// Failed as expected, but does it fail for the expected reason.
|
|
if err != nil && !testCase.shouldPass {
|
|
if !strings.Contains(err.Error(), testCase.err.Error()) {
|
|
t.Errorf("Test %d: %s: Expected to fail with error \"%s\", but instead failed with error \"%s\" instead.", i+1, instanceType, testCase.err.Error(), err.Error())
|
|
}
|
|
}
|
|
// Since there are cases for which GetObject fails, this is
|
|
// necessary. Test passes as expected, but the output values
|
|
// are verified for correctness here.
|
|
if err == nil && testCase.shouldPass {
|
|
if !bytes.Equal(testCase.expectedData, testCase.getObjectData.Bytes()) {
|
|
t.Errorf("Test %d: %s: Data Mismatch: Expected data and the fetched data from GetObject doesn't match.", i+1, instanceType)
|
|
}
|
|
// empty the buffer so that it can be used to further cases.
|
|
testCase.getObjectData.Reset()
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// Wrapper for calling GetObject tests for both Erasure multiple disks and single node setup.
|
|
func TestGetObjectDiskNotFound(t *testing.T) {
|
|
ExecObjectLayerDiskAlteredTest(t, testGetObjectDiskNotFound)
|
|
}
|
|
|
|
// ObjectLayer.GetObject is called with series of cases for valid and erroneous inputs and the result is validated.
|
|
// Before the Get Object call Erasure disks are moved so that the quorum just holds.
|
|
func testGetObjectDiskNotFound(obj ObjectLayer, instanceType string, disks []string, t *testing.T) {
|
|
// Setup for the tests.
|
|
bucketName := getRandomBucketName()
|
|
objectName := "test-object"
|
|
// create bucket.
|
|
err := obj.MakeBucketWithLocation(context.Background(), bucketName, BucketOptions{})
|
|
// Stop the test if creation of the bucket fails.
|
|
if err != nil {
|
|
t.Fatalf("%s : %s", instanceType, err.Error())
|
|
}
|
|
|
|
// set of byte data for PutObject.
|
|
// object has to be created before running tests for GetObject.
|
|
// this is required even to assert the GetObject data,
|
|
// since dataInserted === dataFetched back is a primary criteria for any object storage this assertion is critical.
|
|
bytesData := []struct {
|
|
byteData []byte
|
|
}{
|
|
{generateBytesData(6 * humanize.MiByte)},
|
|
}
|
|
// set of inputs for uploading the objects before tests for downloading is done.
|
|
putObjectInputs := []struct {
|
|
bucketName string
|
|
objectName string
|
|
contentLength int64
|
|
textData []byte
|
|
metaData map[string]string
|
|
}{
|
|
// case - 1.
|
|
{bucketName, objectName, int64(len(bytesData[0].byteData)), bytesData[0].byteData, make(map[string]string)},
|
|
}
|
|
// iterate through the above set of inputs and upkoad the object.
|
|
for i, input := range putObjectInputs {
|
|
// uploading the object.
|
|
_, err = obj.PutObject(context.Background(), input.bucketName, input.objectName, mustGetPutObjReader(t, bytes.NewBuffer(input.textData), input.contentLength, input.metaData["etag"], ""), ObjectOptions{UserDefined: input.metaData})
|
|
// if object upload fails stop the test.
|
|
if err != nil {
|
|
t.Fatalf("Put Object case %d: Error uploading object: <ERROR> %v", i+1, err)
|
|
}
|
|
}
|
|
|
|
// Take 4 disks down before GetObject is called, one more we loose quorum on 16 disk node.
|
|
for _, disk := range disks[:4] {
|
|
os.RemoveAll(disk)
|
|
}
|
|
|
|
// set of empty buffers used to fill GetObject data.
|
|
buffers := []*bytes.Buffer{
|
|
new(bytes.Buffer),
|
|
new(bytes.Buffer),
|
|
}
|
|
|
|
// test cases with set of inputs
|
|
testCases := []struct {
|
|
bucketName string
|
|
objectName string
|
|
startOffset int64
|
|
length int64
|
|
// data obtained/fetched from GetObject.
|
|
getObjectData *bytes.Buffer
|
|
// writer which governs the write into the `getObjectData`.
|
|
writer io.Writer
|
|
// flag indicating whether the test for given ase should pass.
|
|
shouldPass bool
|
|
// expected Result.
|
|
expectedData []byte
|
|
err error
|
|
}{
|
|
// Test case 1-4.
|
|
// Cases with invalid bucket names.
|
|
{".test", "obj", 0, 0, nil, nil, false, []byte(""), fmt.Errorf("%s", "Bucket name invalid: .test")},
|
|
{"------", "obj", 0, 0, nil, nil, false, []byte(""), fmt.Errorf("%s", "Bucket name invalid: ------")},
|
|
{"$this-is-not-valid-too", "obj", 0, 0, nil, nil, false,
|
|
[]byte(""), fmt.Errorf("%s", "Bucket name invalid: $this-is-not-valid-too")},
|
|
{"a", "obj", 0, 0, nil, nil, false, []byte(""), fmt.Errorf("%s", "Bucket name invalid: a")},
|
|
// Test case - 5.
|
|
// Case with invalid object names.
|
|
{bucketName, "", 0, 0, nil, nil, false, []byte(""), fmt.Errorf("%s", "Object name invalid: "+bucketName+"/")},
|
|
// Test case - 7.
|
|
{bucketName, objectName, 0, int64(len(bytesData[0].byteData)), buffers[0], NewEOFWriter(buffers[0], 100), false, []byte{}, io.EOF},
|
|
// Test case with start offset set to 0 and length set to size of the object.
|
|
// Fetching the entire object.
|
|
// Test case - 8.
|
|
{bucketName, objectName, 0, int64(len(bytesData[0].byteData)), buffers[1], buffers[1], true, bytesData[0].byteData, nil},
|
|
// Test case with `length` parameter set to a negative value.
|
|
// Test case - 9.
|
|
{bucketName, objectName, 0, int64(-1), buffers[1], buffers[1], true, bytesData[0].byteData, nil},
|
|
// Test case with `length` parameter set to a negative value and offset is positive.
|
|
// Test case - 10.
|
|
{bucketName, objectName, 1, int64(-1), buffers[1], buffers[1], true, bytesData[0].byteData[1:], nil},
|
|
// Test case with content-range 1 to objectSize .
|
|
// Test case - 11.
|
|
{bucketName, objectName, 1, int64(len(bytesData[0].byteData) - 1), buffers[1], buffers[1], true, bytesData[0].byteData[1:], nil},
|
|
// Test case with content-range 100 to objectSize - 100.
|
|
// Test case - 12.
|
|
{bucketName, objectName, 100, int64(len(bytesData[0].byteData) - 200), buffers[1], buffers[1], true,
|
|
bytesData[0].byteData[100 : len(bytesData[0].byteData)-100], nil},
|
|
// Test case with offset greater than the size of the object
|
|
// Test case - 13.
|
|
{bucketName, objectName, int64(len(bytesData[0].byteData) + 1), int64(len(bytesData[0].byteData)), buffers[0],
|
|
NewEOFWriter(buffers[0], 100), false, []byte{},
|
|
InvalidRange{int64(len(bytesData[0].byteData) + 1), int64(len(bytesData[0].byteData)), int64(len(bytesData[0].byteData))}},
|
|
// Test case with offset greater than the size of the object.
|
|
// Test case - 14.
|
|
{bucketName, objectName, -1, int64(len(bytesData[0].byteData)), buffers[0], new(bytes.Buffer), false, []byte{}, errUnexpected},
|
|
// Test case length parameter is more than the object size.
|
|
// Test case - 15.
|
|
{bucketName, objectName, 0, int64(len(bytesData[0].byteData) + 1), buffers[1], buffers[1], false, bytesData[0].byteData,
|
|
InvalidRange{0, int64(len(bytesData[0].byteData) + 1), int64(len(bytesData[0].byteData))}},
|
|
// Test case with offset + length > objectSize parameter set to a negative value.
|
|
// Test case - 16.
|
|
{bucketName, objectName, 2, int64(len(bytesData[0].byteData)), buffers[1], buffers[1], false, bytesData[0].byteData,
|
|
InvalidRange{2, int64(len(bytesData[0].byteData)), int64(len(bytesData[0].byteData))}},
|
|
// Test case with the writer set to nil.
|
|
// Test case - 17.
|
|
{bucketName, objectName, 0, int64(len(bytesData[0].byteData)), buffers[1], nil, false, bytesData[0].byteData, errUnexpected},
|
|
}
|
|
|
|
for i, testCase := range testCases {
|
|
err = obj.GetObject(context.Background(), testCase.bucketName, testCase.objectName, testCase.startOffset, testCase.length, testCase.writer, "", ObjectOptions{})
|
|
if err != nil && testCase.shouldPass {
|
|
t.Errorf("Test %d: %s: Expected to pass, but failed with: <ERROR> %s", i+1, instanceType, err.Error())
|
|
}
|
|
if err == nil && !testCase.shouldPass {
|
|
t.Errorf("Test %d: %s: Expected to fail with <ERROR> \"%s\", but passed instead.", i+1, instanceType, testCase.err.Error())
|
|
}
|
|
// Failed as expected, but does it fail for the expected reason.
|
|
if err != nil && !testCase.shouldPass {
|
|
if !strings.Contains(err.Error(), testCase.err.Error()) {
|
|
t.Errorf("Test %d: %s: Expected to fail with error \"%s\", but instead failed with error \"%s\" instead.", i+1, instanceType, testCase.err.Error(), err.Error())
|
|
}
|
|
}
|
|
// Since there are cases for which GetObject fails, this is
|
|
// necessary. Test passes as expected, but the output values
|
|
// are verified for correctness here.
|
|
if err == nil && testCase.shouldPass {
|
|
if !bytes.Equal(testCase.expectedData, testCase.getObjectData.Bytes()) {
|
|
t.Errorf("Test %d: %s: Data Mismatch: Expected data and the fetched data from GetObject doesn't match.", i+1, instanceType)
|
|
}
|
|
// empty the buffer so that it can be used to further cases.
|
|
testCase.getObjectData.Reset()
|
|
}
|
|
}
|
|
}
|
|
|
|
// Benchmarks for ObjectLayer.GetObject().
|
|
// The intent is to benchmark GetObject for various sizes ranging from few bytes to 100MB.
|
|
// Also each of these Benchmarks are run both Erasure and FS backends.
|
|
|
|
// BenchmarkGetObjectVerySmallFS - Benchmark FS.GetObject() for object size of 10 bytes.
|
|
func BenchmarkGetObjectVerySmallFS(b *testing.B) {
|
|
benchmarkGetObject(b, "FS", 10)
|
|
}
|
|
|
|
// BenchmarkGetObjectVerySmallErasure - Benchmark Erasure.GetObject() for object size of 10 bytes.
|
|
func BenchmarkGetObjectVerySmallErasure(b *testing.B) {
|
|
benchmarkGetObject(b, "Erasure", 10)
|
|
}
|
|
|
|
// BenchmarkGetObject10KbFS - Benchmark FS.GetObject() for object size of 10KB.
|
|
func BenchmarkGetObject10KbFS(b *testing.B) {
|
|
benchmarkGetObject(b, "FS", 10*humanize.KiByte)
|
|
}
|
|
|
|
// BenchmarkGetObject10KbErasure - Benchmark Erasure.GetObject() for object size of 10KB.
|
|
func BenchmarkGetObject10KbErasure(b *testing.B) {
|
|
benchmarkGetObject(b, "Erasure", 10*humanize.KiByte)
|
|
}
|
|
|
|
// BenchmarkGetObject100KbFS - Benchmark FS.GetObject() for object size of 100KB.
|
|
func BenchmarkGetObject100KbFS(b *testing.B) {
|
|
benchmarkGetObject(b, "FS", 100*humanize.KiByte)
|
|
}
|
|
|
|
// BenchmarkGetObject100KbErasure - Benchmark Erasure.GetObject() for object size of 100KB.
|
|
func BenchmarkGetObject100KbErasure(b *testing.B) {
|
|
benchmarkGetObject(b, "Erasure", 100*humanize.KiByte)
|
|
}
|
|
|
|
// BenchmarkGetObject1MbFS - Benchmark FS.GetObject() for object size of 1MB.
|
|
func BenchmarkGetObject1MbFS(b *testing.B) {
|
|
benchmarkGetObject(b, "FS", 1*humanize.MiByte)
|
|
}
|
|
|
|
// BenchmarkGetObject1MbErasure - Benchmark Erasure.GetObject() for object size of 1MB.
|
|
func BenchmarkGetObject1MbErasure(b *testing.B) {
|
|
benchmarkGetObject(b, "Erasure", 1*humanize.MiByte)
|
|
}
|
|
|
|
// BenchmarkGetObject5MbFS - Benchmark FS.GetObject() for object size of 5MB.
|
|
func BenchmarkGetObject5MbFS(b *testing.B) {
|
|
benchmarkGetObject(b, "FS", 5*humanize.MiByte)
|
|
}
|
|
|
|
// BenchmarkGetObject5MbErasure - Benchmark Erasure.GetObject() for object size of 5MB.
|
|
func BenchmarkGetObject5MbErasure(b *testing.B) {
|
|
benchmarkGetObject(b, "Erasure", 5*humanize.MiByte)
|
|
}
|
|
|
|
// BenchmarkGetObject10MbFS - Benchmark FS.GetObject() for object size of 10MB.
|
|
func BenchmarkGetObject10MbFS(b *testing.B) {
|
|
benchmarkGetObject(b, "FS", 10*humanize.MiByte)
|
|
}
|
|
|
|
// BenchmarkGetObject10MbErasure - Benchmark Erasure.GetObject() for object size of 10MB.
|
|
func BenchmarkGetObject10MbErasure(b *testing.B) {
|
|
benchmarkGetObject(b, "Erasure", 10*humanize.MiByte)
|
|
}
|
|
|
|
// BenchmarkGetObject25MbFS - Benchmark FS.GetObject() for object size of 25MB.
|
|
func BenchmarkGetObject25MbFS(b *testing.B) {
|
|
benchmarkGetObject(b, "FS", 25*humanize.MiByte)
|
|
|
|
}
|
|
|
|
// BenchmarkGetObject25MbErasure - Benchmark Erasure.GetObject() for object size of 25MB.
|
|
func BenchmarkGetObject25MbErasure(b *testing.B) {
|
|
benchmarkGetObject(b, "Erasure", 25*humanize.MiByte)
|
|
}
|
|
|
|
// BenchmarkGetObject50MbFS - Benchmark FS.GetObject() for object size of 50MB.
|
|
func BenchmarkGetObject50MbFS(b *testing.B) {
|
|
benchmarkGetObject(b, "FS", 50*humanize.MiByte)
|
|
}
|
|
|
|
// BenchmarkGetObject50MbErasure - Benchmark Erasure.GetObject() for object size of 50MB.
|
|
func BenchmarkGetObject50MbErasure(b *testing.B) {
|
|
benchmarkGetObject(b, "Erasure", 50*humanize.MiByte)
|
|
}
|
|
|
|
// parallel benchmarks for ObjectLayer.GetObject() .
|
|
|
|
// BenchmarkGetObjectParallelVerySmallFS - Benchmark FS.GetObject() for object size of 10 bytes.
|
|
func BenchmarkGetObjectParallelVerySmallFS(b *testing.B) {
|
|
benchmarkGetObjectParallel(b, "FS", 10)
|
|
}
|
|
|
|
// BenchmarkGetObjectParallelVerySmallErasure - Benchmark Erasure.GetObject() for object size of 10 bytes.
|
|
func BenchmarkGetObjectParallelVerySmallErasure(b *testing.B) {
|
|
benchmarkGetObjectParallel(b, "Erasure", 10)
|
|
}
|
|
|
|
// BenchmarkGetObjectParallel10KbFS - Benchmark FS.GetObject() for object size of 10KB.
|
|
func BenchmarkGetObjectParallel10KbFS(b *testing.B) {
|
|
benchmarkGetObjectParallel(b, "FS", 10*humanize.KiByte)
|
|
}
|
|
|
|
// BenchmarkGetObjectParallel10KbErasure - Benchmark Erasure.GetObject() for object size of 10KB.
|
|
func BenchmarkGetObjectParallel10KbErasure(b *testing.B) {
|
|
benchmarkGetObjectParallel(b, "Erasure", 10*humanize.KiByte)
|
|
}
|
|
|
|
// BenchmarkGetObjectParallel100KbFS - Benchmark FS.GetObject() for object size of 100KB.
|
|
func BenchmarkGetObjectParallel100KbFS(b *testing.B) {
|
|
benchmarkGetObjectParallel(b, "FS", 100*humanize.KiByte)
|
|
}
|
|
|
|
// BenchmarkGetObjectParallel100KbErasure - Benchmark Erasure.GetObject() for object size of 100KB.
|
|
func BenchmarkGetObjectParallel100KbErasure(b *testing.B) {
|
|
benchmarkGetObjectParallel(b, "Erasure", 100*humanize.KiByte)
|
|
}
|
|
|
|
// BenchmarkGetObjectParallel1MbFS - Benchmark FS.GetObject() for object size of 1MB.
|
|
func BenchmarkGetObjectParallel1MbFS(b *testing.B) {
|
|
benchmarkGetObjectParallel(b, "FS", 1*humanize.MiByte)
|
|
}
|
|
|
|
// BenchmarkGetObjectParallel1MbErasure - Benchmark Erasure.GetObject() for object size of 1MB.
|
|
func BenchmarkGetObjectParallel1MbErasure(b *testing.B) {
|
|
benchmarkGetObjectParallel(b, "Erasure", 1*humanize.MiByte)
|
|
}
|
|
|
|
// BenchmarkGetObjectParallel5MbFS - Benchmark FS.GetObject() for object size of 5MB.
|
|
func BenchmarkGetObjectParallel5MbFS(b *testing.B) {
|
|
benchmarkGetObjectParallel(b, "FS", 5*humanize.MiByte)
|
|
}
|
|
|
|
// BenchmarkGetObjectParallel5MbErasure - Benchmark Erasure.GetObject() for object size of 5MB.
|
|
func BenchmarkGetObjectParallel5MbErasure(b *testing.B) {
|
|
benchmarkGetObjectParallel(b, "Erasure", 5*humanize.MiByte)
|
|
}
|
|
|
|
// BenchmarkGetObjectParallel10MbFS - Benchmark FS.GetObject() for object size of 10MB.
|
|
func BenchmarkGetObjectParallel10MbFS(b *testing.B) {
|
|
benchmarkGetObjectParallel(b, "FS", 10*humanize.MiByte)
|
|
}
|
|
|
|
// BenchmarkGetObjectParallel10MbErasure - Benchmark Erasure.GetObject() for object size of 10MB.
|
|
func BenchmarkGetObjectParallel10MbErasure(b *testing.B) {
|
|
benchmarkGetObjectParallel(b, "Erasure", 10*humanize.MiByte)
|
|
}
|
|
|
|
// BenchmarkGetObjectParallel25MbFS - Benchmark FS.GetObject() for object size of 25MB.
|
|
func BenchmarkGetObjectParallel25MbFS(b *testing.B) {
|
|
benchmarkGetObjectParallel(b, "FS", 25*humanize.MiByte)
|
|
|
|
}
|
|
|
|
// BenchmarkGetObjectParallel25MbErasure - Benchmark Erasure.GetObject() for object size of 25MB.
|
|
func BenchmarkGetObjectParallel25MbErasure(b *testing.B) {
|
|
benchmarkGetObjectParallel(b, "Erasure", 25*humanize.MiByte)
|
|
}
|
|
|
|
// BenchmarkGetObjectParallel50MbFS - Benchmark FS.GetObject() for object size of 50MB.
|
|
func BenchmarkGetObjectParallel50MbFS(b *testing.B) {
|
|
benchmarkGetObjectParallel(b, "FS", 50*humanize.MiByte)
|
|
}
|
|
|
|
// BenchmarkGetObjectParallel50MbErasure - Benchmark Erasure.GetObject() for object size of 50MB.
|
|
func BenchmarkGetObjectParallel50MbErasure(b *testing.B) {
|
|
benchmarkGetObjectParallel(b, "Erasure", 50*humanize.MiByte)
|
|
}
|