| 
									
										
										
										
											2016-12-06 01:40:33 +08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2017-01-19 04:24:34 +08:00
										 |  |  |  * Minio Cloud Storage (C) 2016, 2017 Minio, Inc. | 
					
						
							| 
									
										
										
										
											2016-12-06 01:40:33 +08:00
										 |  |  |  * | 
					
						
							|  |  |  |  * 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" | 
					
						
							| 
									
										
										
										
											2017-08-13 10:25:43 +08:00
										 |  |  | 	"os" | 
					
						
							| 
									
										
										
										
											2017-03-16 15:15:06 +08:00
										 |  |  | 	"path" | 
					
						
							|  |  |  | 	"path/filepath" | 
					
						
							| 
									
										
										
										
											2016-12-06 01:40:33 +08:00
										 |  |  | 	"strconv" | 
					
						
							|  |  |  | 	"testing" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // TestListObjectsHeal - Tests ListObjectsHeal API for XL
 | 
					
						
							|  |  |  | func TestListObjectsHeal(t *testing.T) { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-08 04:51:23 +08:00
										 |  |  | 	initNSLock(false) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-19 04:24:34 +08:00
										 |  |  | 	rootPath, err := newTestConfig(globalMinioDefaultRegion) | 
					
						
							| 
									
										
										
										
											2016-12-06 01:40:33 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("Init Test config failed") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// remove the root directory after the test ends.
 | 
					
						
							| 
									
										
										
										
											2017-08-13 10:25:43 +08:00
										 |  |  | 	defer os.RemoveAll(rootPath) | 
					
						
							| 
									
										
										
										
											2016-12-06 01:40:33 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Create an instance of xl backend
 | 
					
						
							|  |  |  | 	xl, fsDirs, err := prepareXL() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Cleanup backend directories
 | 
					
						
							|  |  |  | 	defer removeRoots(fsDirs) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bucketName := "bucket" | 
					
						
							|  |  |  | 	objName := "obj" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Create test bucket
 | 
					
						
							| 
									
										
										
										
											2017-06-02 00:43:20 +08:00
										 |  |  | 	err = xl.MakeBucketWithLocation(bucketName, "") | 
					
						
							| 
									
										
										
										
											2016-12-06 01:40:33 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-08 04:51:23 +08:00
										 |  |  | 	// Put 5 objects under sane dir
 | 
					
						
							|  |  |  | 	for i := 0; i < 5; i++ { | 
					
						
							| 
									
										
										
										
											2017-09-20 03:40:27 +08:00
										 |  |  | 		_, err = xl.PutObject(bucketName, "sane/"+objName+strconv.Itoa(i), NewHashReader(bytes.NewReader([]byte("abcd")), int64(len("abcd")), "", ""), nil) | 
					
						
							| 
									
										
										
										
											2016-12-06 01:40:33 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			t.Fatalf("XL Object upload failed: <ERROR> %s", err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Put 500 objects under unsane/subdir dir
 | 
					
						
							| 
									
										
										
										
											2017-02-08 04:51:23 +08:00
										 |  |  | 	for i := 0; i < 5; i++ { | 
					
						
							| 
									
										
										
										
											2017-09-20 03:40:27 +08:00
										 |  |  | 		_, err = xl.PutObject(bucketName, "unsane/subdir/"+objName+strconv.Itoa(i), NewHashReader(bytes.NewReader([]byte("abcd")), int64(len("abcd")), "", ""), nil) | 
					
						
							| 
									
										
										
										
											2016-12-06 01:40:33 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			t.Fatalf("XL Object upload failed: <ERROR> %s", err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Structure for testing
 | 
					
						
							|  |  |  | 	type testData struct { | 
					
						
							|  |  |  | 		bucket      string | 
					
						
							|  |  |  | 		object      string | 
					
						
							|  |  |  | 		marker      string | 
					
						
							|  |  |  | 		delimiter   string | 
					
						
							|  |  |  | 		maxKeys     int | 
					
						
							|  |  |  | 		expectedErr error | 
					
						
							|  |  |  | 		foundObjs   int | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Generic function for testing ListObjectsHeal, needs testData as a parameter
 | 
					
						
							|  |  |  | 	testFunc := func(testCase testData, testRank int) { | 
					
						
							|  |  |  | 		objectsNeedHeal, foundErr := xl.ListObjectsHeal(testCase.bucket, testCase.object, testCase.marker, testCase.delimiter, testCase.maxKeys) | 
					
						
							|  |  |  | 		if testCase.expectedErr == nil && foundErr != nil { | 
					
						
							|  |  |  | 			t.Fatalf("Test %d: Expected nil error, found: %v", testRank, foundErr) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if testCase.expectedErr != nil && foundErr.Error() != testCase.expectedErr.Error() { | 
					
						
							|  |  |  | 			t.Fatalf("Test %d: Found unexpected error: %v, expected: %v", testRank, foundErr, testCase.expectedErr) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if len(objectsNeedHeal.Objects) != testCase.foundObjs { | 
					
						
							|  |  |  | 			t.Fatalf("Test %d: Found unexpected number of objects: %d, expected: %v", testRank, len(objectsNeedHeal.Objects), testCase.foundObjs) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Start tests
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	testCases := []testData{ | 
					
						
							|  |  |  | 		// Wrong bucket name
 | 
					
						
							|  |  |  | 		{"foobucket", "", "", "", 1000, BucketNotFound{Bucket: "foobucket"}, 0}, | 
					
						
							|  |  |  | 		// Inexistent object
 | 
					
						
							|  |  |  | 		{bucketName, "inexistentObj", "", "", 1000, nil, 0}, | 
					
						
							|  |  |  | 		// Test ListObjectsHeal when all objects are sane
 | 
					
						
							|  |  |  | 		{bucketName, "", "", "", 1000, nil, 0}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for i, testCase := range testCases { | 
					
						
							|  |  |  | 		testFunc(testCase, i+1) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Test ListObjectsHeal when all objects under unsane need healing
 | 
					
						
							|  |  |  | 	xlObj := xl.(*xlObjects) | 
					
						
							| 
									
										
										
										
											2017-02-08 04:51:23 +08:00
										 |  |  | 	for i := 0; i < 5; i++ { | 
					
						
							| 
									
										
										
										
											2016-12-06 01:40:33 +08:00
										 |  |  | 		if err = xlObj.storageDisks[0].DeleteFile(bucketName, "unsane/subdir/"+objName+strconv.Itoa(i)+"/xl.json"); err != nil { | 
					
						
							|  |  |  | 			t.Fatal(err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Start tests again with some objects that need healing
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	testCases = []testData{ | 
					
						
							|  |  |  | 		// Test ListObjectsHeal when all objects under unsane/ need to be healed
 | 
					
						
							| 
									
										
										
										
											2017-02-08 04:51:23 +08:00
										 |  |  | 		{bucketName, "", "", "", 1000, nil, 5}, | 
					
						
							| 
									
										
										
										
											2016-12-06 01:40:33 +08:00
										 |  |  | 		// List objects heal under unsane/, should return all elements
 | 
					
						
							| 
									
										
										
										
											2017-02-08 04:51:23 +08:00
										 |  |  | 		{bucketName, "unsane/", "", "", 1000, nil, 5}, | 
					
						
							| 
									
										
										
										
											2016-12-06 01:40:33 +08:00
										 |  |  | 		// List healing objects under sane/, should return 0
 | 
					
						
							|  |  |  | 		{bucketName, "sane/", "", "", 1000, nil, 0}, | 
					
						
							|  |  |  | 		// Max Keys == 200
 | 
					
						
							| 
									
										
										
										
											2017-02-08 04:51:23 +08:00
										 |  |  | 		{bucketName, "unsane/", "", "", 2, nil, 2}, | 
					
						
							| 
									
										
										
										
											2016-12-06 01:40:33 +08:00
										 |  |  | 		// Max key > 1000
 | 
					
						
							| 
									
										
										
										
											2017-02-08 04:51:23 +08:00
										 |  |  | 		{bucketName, "unsane/", "", "", 5000, nil, 5}, | 
					
						
							| 
									
										
										
										
											2016-12-06 01:40:33 +08:00
										 |  |  | 		// Prefix == Delimiter == "/"
 | 
					
						
							| 
									
										
										
										
											2017-02-08 04:51:23 +08:00
										 |  |  | 		{bucketName, "/", "", "/", 1000, nil, 0}, | 
					
						
							| 
									
										
										
										
											2016-12-06 01:40:33 +08:00
										 |  |  | 		// Max Keys == 0
 | 
					
						
							|  |  |  | 		{bucketName, "", "", "", 0, nil, 0}, | 
					
						
							|  |  |  | 		// Testing with marker parameter
 | 
					
						
							| 
									
										
										
										
											2017-02-08 04:51:23 +08:00
										 |  |  | 		{bucketName, "", "unsane/subdir/" + objName + "0", "", 1000, nil, 4}, | 
					
						
							| 
									
										
										
										
											2016-12-06 01:40:33 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	for i, testCase := range testCases { | 
					
						
							|  |  |  | 		testFunc(testCase, i+1) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-03-16 15:15:06 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Test for ListUploadsHeal API for XL.
 | 
					
						
							|  |  |  | func TestListUploadsHeal(t *testing.T) { | 
					
						
							|  |  |  | 	initNSLock(false) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rootPath, err := newTestConfig(globalMinioDefaultRegion) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("Init Test config failed") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Remove config directory after the test ends.
 | 
					
						
							| 
									
										
										
										
											2017-08-13 10:25:43 +08:00
										 |  |  | 	defer os.RemoveAll(rootPath) | 
					
						
							| 
									
										
										
										
											2017-03-16 15:15:06 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Create an instance of XL backend.
 | 
					
						
							|  |  |  | 	xl, fsDirs, err := prepareXL() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Cleanup backend directories on function return.
 | 
					
						
							|  |  |  | 	defer removeRoots(fsDirs) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bucketName := "bucket" | 
					
						
							|  |  |  | 	prefix := "prefix" | 
					
						
							|  |  |  | 	objName := path.Join(prefix, "obj") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Create test bucket.
 | 
					
						
							| 
									
										
										
										
											2017-06-02 00:43:20 +08:00
										 |  |  | 	err = xl.MakeBucketWithLocation(bucketName, "") | 
					
						
							| 
									
										
										
										
											2017-03-16 15:15:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Create a new multipart upload.
 | 
					
						
							|  |  |  | 	uploadID, err := xl.NewMultipartUpload(bucketName, objName, nil) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Upload a part.
 | 
					
						
							|  |  |  | 	data := bytes.Repeat([]byte("a"), 1024) | 
					
						
							|  |  |  | 	_, err = xl.PutObjectPart(bucketName, objName, uploadID, 1, | 
					
						
							| 
									
										
										
										
											2017-09-20 03:40:27 +08:00
										 |  |  | 		NewHashReader(bytes.NewReader(data), int64(len(data)), "", "")) | 
					
						
							| 
									
										
										
										
											2017-03-16 15:15:06 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Check if list uploads heal returns any uploads to be healed
 | 
					
						
							|  |  |  | 	// incorrectly.
 | 
					
						
							|  |  |  | 	listUploadsInfo, err := xl.ListUploadsHeal(bucketName, prefix, "", "", "", 1000) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// All uploads intact nothing to heal.
 | 
					
						
							|  |  |  | 	if len(listUploadsInfo.Uploads) != 0 { | 
					
						
							|  |  |  | 		t.Errorf("Expected no uploads but received %d", len(listUploadsInfo.Uploads)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Delete the part from the first disk to make the upload (and
 | 
					
						
							|  |  |  | 	// its part) to appear in upload heal listing.
 | 
					
						
							|  |  |  | 	firstDisk := xl.(*xlObjects).storageDisks[0] | 
					
						
							|  |  |  | 	err = firstDisk.DeleteFile(minioMetaMultipartBucket, | 
					
						
							|  |  |  | 		filepath.Join(bucketName, objName, uploadID, xlMetaJSONFile)) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	listUploadsInfo, err = xl.ListUploadsHeal(bucketName, prefix, "", "", "", 1000) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatal(err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// One upload with missing xl.json on first disk.
 | 
					
						
							|  |  |  | 	if len(listUploadsInfo.Uploads) != 1 { | 
					
						
							|  |  |  | 		t.Errorf("Expected 1 upload but received %d", len(listUploadsInfo.Uploads)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |