| 
									
										
										
										
											2016-07-13 06:20:31 +08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  |  * MinIO Cloud Storage, (C) 2016-2020 MinIO, Inc. | 
					
						
							| 
									
										
										
										
											2016-07-13 06:20:31 +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. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-19 07:23:42 +08:00
										 |  |  | package cmd | 
					
						
							| 
									
										
										
										
											2016-07-13 06:20:31 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2017-03-05 06:53:28 +08:00
										 |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2020-04-15 08:52:38 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2020-03-03 08:29:30 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	"os" | 
					
						
							| 
									
										
										
										
											2017-03-05 06:53:28 +08:00
										 |  |  | 	"path/filepath" | 
					
						
							| 
									
										
										
										
											2016-07-13 06:20:31 +08:00
										 |  |  | 	"testing" | 
					
						
							|  |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2019-03-15 04:08:51 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/minio/minio/pkg/madmin" | 
					
						
							| 
									
										
										
										
											2016-07-13 06:20:31 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // validates functionality provided to find most common
 | 
					
						
							|  |  |  | // time occurrence from a list of time.
 | 
					
						
							|  |  |  | func TestCommonTime(t *testing.T) { | 
					
						
							|  |  |  | 	// List of test cases for common modTime.
 | 
					
						
							|  |  |  | 	testCases := []struct { | 
					
						
							|  |  |  | 		times []time.Time | 
					
						
							|  |  |  | 		time  time.Time | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			// 1. Tests common times when slice has varying time elements.
 | 
					
						
							|  |  |  | 			[]time.Time{ | 
					
						
							|  |  |  | 				time.Unix(0, 1).UTC(), | 
					
						
							|  |  |  | 				time.Unix(0, 2).UTC(), | 
					
						
							|  |  |  | 				time.Unix(0, 3).UTC(), | 
					
						
							|  |  |  | 				time.Unix(0, 3).UTC(), | 
					
						
							|  |  |  | 				time.Unix(0, 2).UTC(), | 
					
						
							|  |  |  | 				time.Unix(0, 3).UTC(), | 
					
						
							|  |  |  | 				time.Unix(0, 1).UTC(), | 
					
						
							|  |  |  | 			}, time.Unix(0, 3).UTC(), | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			// 2. Tests common time obtained when all elements are equal.
 | 
					
						
							|  |  |  | 			[]time.Time{ | 
					
						
							|  |  |  | 				time.Unix(0, 3).UTC(), | 
					
						
							|  |  |  | 				time.Unix(0, 3).UTC(), | 
					
						
							|  |  |  | 				time.Unix(0, 3).UTC(), | 
					
						
							|  |  |  | 				time.Unix(0, 3).UTC(), | 
					
						
							|  |  |  | 				time.Unix(0, 3).UTC(), | 
					
						
							|  |  |  | 				time.Unix(0, 3).UTC(), | 
					
						
							|  |  |  | 				time.Unix(0, 3).UTC(), | 
					
						
							|  |  |  | 			}, time.Unix(0, 3).UTC(), | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			// 3. Tests common time obtained when elements have a mixture
 | 
					
						
							|  |  |  | 			// of sentinel values.
 | 
					
						
							|  |  |  | 			[]time.Time{ | 
					
						
							|  |  |  | 				time.Unix(0, 3).UTC(), | 
					
						
							|  |  |  | 				time.Unix(0, 3).UTC(), | 
					
						
							|  |  |  | 				time.Unix(0, 2).UTC(), | 
					
						
							|  |  |  | 				time.Unix(0, 1).UTC(), | 
					
						
							|  |  |  | 				time.Unix(0, 3).UTC(), | 
					
						
							|  |  |  | 				time.Unix(0, 4).UTC(), | 
					
						
							|  |  |  | 				time.Unix(0, 3).UTC(), | 
					
						
							|  |  |  | 				timeSentinel, | 
					
						
							|  |  |  | 				timeSentinel, | 
					
						
							|  |  |  | 				timeSentinel, | 
					
						
							|  |  |  | 			}, time.Unix(0, 3).UTC(), | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Tests all the testcases, and validates them against expected
 | 
					
						
							|  |  |  | 	// common modtime. Tests fail if modtime does not match.
 | 
					
						
							|  |  |  | 	for i, testCase := range testCases { | 
					
						
							|  |  |  | 		// Obtain a common mod time from modTimes slice.
 | 
					
						
							| 
									
										
										
										
											2017-01-18 02:02:58 +08:00
										 |  |  | 		ctime, _ := commonTime(testCase.times) | 
					
						
							| 
									
										
										
										
											2020-08-25 03:11:20 +08:00
										 |  |  | 		if !testCase.time.Equal(ctime) { | 
					
						
							| 
									
										
										
										
											2016-07-13 06:20:31 +08:00
										 |  |  | 			t.Fatalf("Test case %d, expect to pass but failed. Wanted modTime: %s, got modTime: %s\n", i+1, testCase.time, ctime) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-03-05 06:53:28 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // TestListOnlineDisks - checks if listOnlineDisks and outDatedDisks
 | 
					
						
							|  |  |  | // are consistent with each other.
 | 
					
						
							|  |  |  | func TestListOnlineDisks(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2020-04-15 08:52:38 +08:00
										 |  |  | 	ctx, cancel := context.WithCancel(context.Background()) | 
					
						
							|  |  |  | 	defer cancel() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	obj, disks, err := prepareErasure16(ctx) | 
					
						
							| 
									
										
										
										
											2017-03-05 06:53:28 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 		t.Fatalf("Prepare Erasure backend failed - %v", err) | 
					
						
							| 
									
										
										
										
											2017-03-05 06:53:28 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	defer removeRoots(disks) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	type tamperKind int | 
					
						
							|  |  |  | 	const ( | 
					
						
							|  |  |  | 		noTamper    tamperKind = iota | 
					
						
							|  |  |  | 		deletePart  tamperKind = iota | 
					
						
							|  |  |  | 		corruptPart tamperKind = iota | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	threeNanoSecs := time.Unix(0, 3).UTC() | 
					
						
							|  |  |  | 	fourNanoSecs := time.Unix(0, 4).UTC() | 
					
						
							|  |  |  | 	modTimesThreeNone := []time.Time{ | 
					
						
							|  |  |  | 		threeNanoSecs, threeNanoSecs, threeNanoSecs, threeNanoSecs, | 
					
						
							|  |  |  | 		threeNanoSecs, threeNanoSecs, threeNanoSecs, | 
					
						
							|  |  |  | 		timeSentinel, timeSentinel, timeSentinel, timeSentinel, | 
					
						
							|  |  |  | 		timeSentinel, timeSentinel, timeSentinel, timeSentinel, | 
					
						
							|  |  |  | 		timeSentinel, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	modTimesThreeFour := []time.Time{ | 
					
						
							|  |  |  | 		threeNanoSecs, threeNanoSecs, threeNanoSecs, threeNanoSecs, | 
					
						
							|  |  |  | 		threeNanoSecs, threeNanoSecs, threeNanoSecs, threeNanoSecs, | 
					
						
							|  |  |  | 		fourNanoSecs, fourNanoSecs, fourNanoSecs, fourNanoSecs, | 
					
						
							|  |  |  | 		fourNanoSecs, fourNanoSecs, fourNanoSecs, fourNanoSecs, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	testCases := []struct { | 
					
						
							|  |  |  | 		modTimes       []time.Time | 
					
						
							|  |  |  | 		expectedTime   time.Time | 
					
						
							|  |  |  | 		errs           []error | 
					
						
							|  |  |  | 		_tamperBackend tamperKind | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			modTimes:     modTimesThreeFour, | 
					
						
							|  |  |  | 			expectedTime: fourNanoSecs, | 
					
						
							|  |  |  | 			errs: []error{ | 
					
						
							|  |  |  | 				nil, nil, nil, nil, nil, nil, nil, nil, nil, | 
					
						
							|  |  |  | 				nil, nil, nil, nil, nil, nil, nil, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			_tamperBackend: noTamper, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			modTimes:     modTimesThreeNone, | 
					
						
							|  |  |  | 			expectedTime: threeNanoSecs, | 
					
						
							|  |  |  | 			errs: []error{ | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 				// Disks that have a valid xl.meta.
 | 
					
						
							| 
									
										
										
										
											2017-03-05 06:53:28 +08:00
										 |  |  | 				nil, nil, nil, nil, nil, nil, nil, | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 				// Majority of disks don't have xl.meta.
 | 
					
						
							| 
									
										
										
										
											2017-03-05 06:53:28 +08:00
										 |  |  | 				errFileNotFound, errFileNotFound, | 
					
						
							|  |  |  | 				errFileNotFound, errFileNotFound, | 
					
						
							|  |  |  | 				errFileNotFound, errDiskAccessDenied, | 
					
						
							|  |  |  | 				errDiskNotFound, errFileNotFound, | 
					
						
							|  |  |  | 				errFileNotFound, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			_tamperBackend: deletePart, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			modTimes:     modTimesThreeNone, | 
					
						
							|  |  |  | 			expectedTime: threeNanoSecs, | 
					
						
							|  |  |  | 			errs: []error{ | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 				// Disks that have a valid xl.meta.
 | 
					
						
							| 
									
										
										
										
											2017-03-05 06:53:28 +08:00
										 |  |  | 				nil, nil, nil, nil, nil, nil, nil, | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 				// Majority of disks don't have xl.meta.
 | 
					
						
							| 
									
										
										
										
											2017-03-05 06:53:28 +08:00
										 |  |  | 				errFileNotFound, errFileNotFound, | 
					
						
							|  |  |  | 				errFileNotFound, errFileNotFound, | 
					
						
							|  |  |  | 				errFileNotFound, errDiskAccessDenied, | 
					
						
							|  |  |  | 				errDiskNotFound, errFileNotFound, | 
					
						
							|  |  |  | 				errFileNotFound, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			_tamperBackend: corruptPart, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bucket := "bucket" | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	err = obj.MakeBucketWithLocation(ctx, "bucket", BucketOptions{}) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("Failed to make a bucket %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-05 06:53:28 +08:00
										 |  |  | 	object := "object" | 
					
						
							|  |  |  | 	data := bytes.Repeat([]byte("a"), 1024) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	z := obj.(*erasureZones) | 
					
						
							|  |  |  | 	erasureDisks := z.zones[0].sets[0].getDisks() | 
					
						
							| 
									
										
										
										
											2017-03-05 06:53:28 +08:00
										 |  |  | 	for i, test := range testCases { | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 		_, err = obj.PutObject(ctx, bucket, object, mustGetPutObjReader(t, bytes.NewReader(data), int64(len(data)), "", ""), ObjectOptions{}) | 
					
						
							| 
									
										
										
										
											2017-03-05 06:53:28 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 			t.Fatalf("Failed to putObject %v", err) | 
					
						
							| 
									
										
										
										
											2017-03-05 06:53:28 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 		partsMetadata, errs := readAllFileInfo(ctx, erasureDisks, bucket, object, "") | 
					
						
							|  |  |  | 		fi, err := getLatestFileInfo(ctx, partsMetadata, errs) | 
					
						
							| 
									
										
										
										
											2017-03-05 06:53:28 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 			t.Fatalf("Failed to getLatestFileInfo %v", err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for j := range partsMetadata { | 
					
						
							|  |  |  | 			if errs[j] != nil { | 
					
						
							|  |  |  | 				t.Fatalf("Test %d: expected error to be nil: %s", i+1, errs[j]) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			partsMetadata[j].ModTime = test.modTimes[j] | 
					
						
							| 
									
										
										
										
											2017-03-05 06:53:28 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		tamperedIndex := -1 | 
					
						
							|  |  |  | 		switch test._tamperBackend { | 
					
						
							|  |  |  | 		case deletePart: | 
					
						
							|  |  |  | 			for index, err := range test.errs { | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				// Remove a part from a disk
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 				// which has a valid xl.meta,
 | 
					
						
							| 
									
										
										
										
											2017-03-05 06:53:28 +08:00
										 |  |  | 				// and check if that disk
 | 
					
						
							|  |  |  | 				// appears in outDatedDisks.
 | 
					
						
							|  |  |  | 				tamperedIndex = index | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 				dErr := erasureDisks[index].DeleteFile(bucket, pathJoin(object, fi.DataDir, "part.1")) | 
					
						
							| 
									
										
										
										
											2017-03-05 06:53:28 +08:00
										 |  |  | 				if dErr != nil { | 
					
						
							|  |  |  | 					t.Fatalf("Test %d: Failed to delete %s - %v", i+1, | 
					
						
							|  |  |  | 						filepath.Join(object, "part.1"), dErr) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				break | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		case corruptPart: | 
					
						
							|  |  |  | 			for index, err := range test.errs { | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				// Corrupt a part from a disk
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 				// which has a valid xl.meta,
 | 
					
						
							| 
									
										
										
										
											2017-03-05 06:53:28 +08:00
										 |  |  | 				// and check if that disk
 | 
					
						
							|  |  |  | 				// appears in outDatedDisks.
 | 
					
						
							|  |  |  | 				tamperedIndex = index | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 				filePath := pathJoin(erasureDisks[index].String(), bucket, object, fi.DataDir, "part.1") | 
					
						
							| 
									
										
										
										
											2019-11-05 01:30:59 +08:00
										 |  |  | 				f, err := os.OpenFile(filePath, os.O_WRONLY|os.O_SYNC, 0) | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					t.Fatalf("Failed to open %s: %s\n", filePath, err) | 
					
						
							| 
									
										
										
										
											2017-03-05 06:53:28 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 				f.Write([]byte("oops")) // Will cause bitrot error
 | 
					
						
							|  |  |  | 				f.Close() | 
					
						
							| 
									
										
										
										
											2017-03-05 06:53:28 +08:00
										 |  |  | 				break | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 		onlineDisks, modTime := listOnlineDisks(erasureDisks, partsMetadata, test.errs) | 
					
						
							| 
									
										
										
										
											2017-03-05 06:53:28 +08:00
										 |  |  | 		if !modTime.Equal(test.expectedTime) { | 
					
						
							|  |  |  | 			t.Fatalf("Test %d: Expected modTime to be equal to %v but was found to be %v", | 
					
						
							|  |  |  | 				i+1, test.expectedTime, modTime) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 		availableDisks, newErrs := disksWithAllParts(ctx, onlineDisks, partsMetadata, test.errs, bucket, object, madmin.HealDeepScan) | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 		test.errs = newErrs | 
					
						
							| 
									
										
										
										
											2017-03-05 06:53:28 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 		if test._tamperBackend != noTamper { | 
					
						
							|  |  |  | 			if tamperedIndex != -1 && availableDisks[tamperedIndex] != nil { | 
					
						
							|  |  |  | 				t.Fatalf("Test %d: disk (%v) with part.1 missing is not a disk with available data", | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 					i+1, erasureDisks[tamperedIndex]) | 
					
						
							| 
									
										
										
										
											2017-03-05 06:53:28 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-01-23 06:54:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-05 06:53:28 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-06-15 08:13:02 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | func TestDisksWithAllParts(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2020-04-15 08:52:38 +08:00
										 |  |  | 	ctx, cancel := context.WithCancel(context.Background()) | 
					
						
							|  |  |  | 	defer cancel() | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	obj, disks, err := prepareErasure16(ctx) | 
					
						
							| 
									
										
										
										
											2017-06-15 08:13:02 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 		t.Fatalf("Prepare Erasure backend failed - %v", err) | 
					
						
							| 
									
										
										
										
											2017-06-15 08:13:02 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	defer removeRoots(disks) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bucket := "bucket" | 
					
						
							|  |  |  | 	object := "object" | 
					
						
							|  |  |  | 	// make data with more than one part
 | 
					
						
							|  |  |  | 	partCount := 3 | 
					
						
							| 
									
										
										
										
											2019-11-20 09:42:27 +08:00
										 |  |  | 	data := bytes.Repeat([]byte("a"), 6*1024*1024*partCount) | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	z := obj.(*erasureZones) | 
					
						
							|  |  |  | 	s := z.zones[0].sets[0] | 
					
						
							|  |  |  | 	erasureDisks := s.getDisks() | 
					
						
							|  |  |  | 	err = obj.MakeBucketWithLocation(ctx, "bucket", BucketOptions{}) | 
					
						
							| 
									
										
										
										
											2017-06-15 08:13:02 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("Failed to make a bucket %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-09 13:31:06 +08:00
										 |  |  | 	_, err = obj.PutObject(ctx, bucket, object, mustGetPutObjReader(t, bytes.NewReader(data), int64(len(data)), "", ""), ObjectOptions{}) | 
					
						
							| 
									
										
										
										
											2017-06-15 08:13:02 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("Failed to putObject %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	_, errs := readAllFileInfo(ctx, erasureDisks, bucket, object, "") | 
					
						
							|  |  |  | 	readQuorum := len(erasureDisks) / 2 | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	if reducedErr := reduceReadQuorumErrs(ctx, errs, objectOpIgnoredErrs, readQuorum); reducedErr != nil { | 
					
						
							| 
									
										
										
										
											2017-08-24 08:58:52 +08:00
										 |  |  | 		t.Fatalf("Failed to read xl meta data %v", reducedErr) | 
					
						
							| 
									
										
										
										
											2017-06-15 08:13:02 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	// Test that all disks are returned without any failures with
 | 
					
						
							|  |  |  | 	// unmodified meta data
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	partsMetadata, errs := readAllFileInfo(ctx, erasureDisks, bucket, object, "") | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("Failed to read xl meta data %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	filteredDisks, errs := disksWithAllParts(ctx, erasureDisks, partsMetadata, errs, bucket, object, madmin.HealDeepScan) | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	if len(filteredDisks) != len(erasureDisks) { | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 		t.Errorf("Unexpected number of disks: %d", len(filteredDisks)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for diskIndex, disk := range filteredDisks { | 
					
						
							|  |  |  | 		if errs[diskIndex] != nil { | 
					
						
							|  |  |  | 			t.Errorf("Unexpected error %s", errs[diskIndex]) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if disk == nil { | 
					
						
							|  |  |  | 			t.Errorf("Disk erroneously filtered, diskIndex: %d", diskIndex) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-06-15 08:13:02 +08:00
										 |  |  | 	diskFailures := make(map[int]string) | 
					
						
							|  |  |  | 	// key = disk index, value = part name with hash mismatch
 | 
					
						
							| 
									
										
										
										
											2019-05-15 03:33:18 +08:00
										 |  |  | 	diskFailures[0] = "part.1" | 
					
						
							| 
									
										
										
										
											2017-06-15 08:13:02 +08:00
										 |  |  | 	diskFailures[3] = "part.1" | 
					
						
							| 
									
										
										
										
											2019-05-15 03:33:18 +08:00
										 |  |  | 	diskFailures[15] = "part.1" | 
					
						
							| 
									
										
										
										
											2017-06-15 08:13:02 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	for diskIndex, partName := range diskFailures { | 
					
						
							| 
									
										
										
										
											2020-03-03 08:29:30 +08:00
										 |  |  | 		for i := range partsMetadata[diskIndex].Erasure.Checksums { | 
					
						
							|  |  |  | 			if fmt.Sprintf("part.%d", i+1) == partName { | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 				filePath := pathJoin(erasureDisks[diskIndex].String(), bucket, object, partsMetadata[diskIndex].DataDir, partName) | 
					
						
							| 
									
										
										
										
											2019-11-05 01:30:59 +08:00
										 |  |  | 				f, err := os.OpenFile(filePath, os.O_WRONLY|os.O_SYNC, 0) | 
					
						
							| 
									
										
										
										
											2019-01-17 20:58:18 +08:00
										 |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					t.Fatalf("Failed to open %s: %s\n", filePath, err) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				f.Write([]byte("oops")) // Will cause bitrot error
 | 
					
						
							|  |  |  | 				f.Close() | 
					
						
							| 
									
										
										
										
											2017-06-15 08:13:02 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	errs = make([]error, len(erasureDisks)) | 
					
						
							|  |  |  | 	filteredDisks, errs = disksWithAllParts(ctx, erasureDisks, partsMetadata, errs, bucket, object, madmin.HealDeepScan) | 
					
						
							| 
									
										
										
										
											2017-06-15 08:13:02 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	if len(filteredDisks) != len(erasureDisks) { | 
					
						
							| 
									
										
										
										
											2017-06-15 08:13:02 +08:00
										 |  |  | 		t.Errorf("Unexpected number of disks: %d", len(filteredDisks)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for diskIndex, disk := range filteredDisks { | 
					
						
							|  |  |  | 		if _, ok := diskFailures[diskIndex]; ok { | 
					
						
							|  |  |  | 			if disk != nil { | 
					
						
							|  |  |  | 				t.Errorf("Disk not filtered as expected, disk: %d", diskIndex) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if errs[diskIndex] == nil { | 
					
						
							|  |  |  | 				t.Errorf("Expected error not received, diskIndex: %d", diskIndex) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			if disk == nil { | 
					
						
							|  |  |  | 				t.Errorf("Disk erroneously filtered, diskIndex: %d", diskIndex) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if errs[diskIndex] != nil { | 
					
						
							|  |  |  | 				t.Errorf("Unexpected error, %s, diskIndex: %d", errs[diskIndex], diskIndex) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |