| 
									
										
										
										
											2016-06-26 18:32:49 +08:00
										 |  |  | /* | 
					
						
							| 
									
										
										
										
											2019-04-10 02:39:42 +08:00
										 |  |  |  * MinIO Cloud Storage, (C) 2016 MinIO, Inc. | 
					
						
							| 
									
										
										
										
											2016-06-26 18:32:49 +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-06-26 18:32:49 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2017-08-15 09:08:42 +08:00
										 |  |  | 	"crypto/rand" | 
					
						
							|  |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2017-08-13 10:25:43 +08:00
										 |  |  | 	"os" | 
					
						
							| 
									
										
										
										
											2016-06-26 18:32:49 +08:00
										 |  |  | 	"testing" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-24 14:35:37 +08:00
										 |  |  | var erasureEncodeDecodeTests = []struct { | 
					
						
							| 
									
										
										
										
											2017-08-15 09:08:42 +08:00
										 |  |  | 	dataBlocks, parityBlocks   int | 
					
						
							|  |  |  | 	missingData, missingParity int | 
					
						
							|  |  |  | 	reconstructParity          bool | 
					
						
							|  |  |  | 	shouldFail                 bool | 
					
						
							|  |  |  | }{ | 
					
						
							|  |  |  | 	{dataBlocks: 2, parityBlocks: 2, missingData: 0, missingParity: 0, reconstructParity: true, shouldFail: false}, | 
					
						
							|  |  |  | 	{dataBlocks: 3, parityBlocks: 3, missingData: 1, missingParity: 0, reconstructParity: true, shouldFail: false}, | 
					
						
							|  |  |  | 	{dataBlocks: 4, parityBlocks: 4, missingData: 2, missingParity: 0, reconstructParity: false, shouldFail: false}, | 
					
						
							|  |  |  | 	{dataBlocks: 5, parityBlocks: 5, missingData: 0, missingParity: 1, reconstructParity: true, shouldFail: false}, | 
					
						
							|  |  |  | 	{dataBlocks: 6, parityBlocks: 6, missingData: 0, missingParity: 2, reconstructParity: true, shouldFail: false}, | 
					
						
							|  |  |  | 	{dataBlocks: 7, parityBlocks: 7, missingData: 1, missingParity: 1, reconstructParity: false, shouldFail: false}, | 
					
						
							|  |  |  | 	{dataBlocks: 8, parityBlocks: 8, missingData: 3, missingParity: 2, reconstructParity: false, shouldFail: false}, | 
					
						
							|  |  |  | 	{dataBlocks: 2, parityBlocks: 2, missingData: 2, missingParity: 1, reconstructParity: true, shouldFail: true}, | 
					
						
							|  |  |  | 	{dataBlocks: 4, parityBlocks: 2, missingData: 2, missingParity: 2, reconstructParity: false, shouldFail: true}, | 
					
						
							|  |  |  | 	{dataBlocks: 8, parityBlocks: 4, missingData: 2, missingParity: 2, reconstructParity: false, shouldFail: false}, | 
					
						
							| 
									
										
										
										
											2016-06-26 18:32:49 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-24 14:35:37 +08:00
										 |  |  | func TestErasureEncodeDecode(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2017-08-15 09:08:42 +08:00
										 |  |  | 	data := make([]byte, 256) | 
					
						
							|  |  |  | 	if _, err := io.ReadFull(rand.Reader, data); err != nil { | 
					
						
							|  |  |  | 		t.Fatalf("Failed to read random data: %v", err) | 
					
						
							| 
									
										
										
										
											2016-06-26 18:32:49 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-08-24 14:35:37 +08:00
										 |  |  | 	for i, test := range erasureEncodeDecodeTests { | 
					
						
							| 
									
										
										
										
											2017-08-15 09:08:42 +08:00
										 |  |  | 		buffer := make([]byte, len(data), 2*len(data)) | 
					
						
							|  |  |  | 		copy(buffer, data) | 
					
						
							| 
									
										
										
										
											2016-06-26 18:32:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-24 14:35:37 +08:00
										 |  |  | 		erasure, err := NewErasure(context.Background(), test.dataBlocks, test.parityBlocks, blockSizeV1) | 
					
						
							| 
									
										
										
										
											2017-08-15 09:08:42 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2018-08-24 14:35:37 +08:00
										 |  |  | 			t.Fatalf("Test %d: failed to create erasure: %v", i, err) | 
					
						
							| 
									
										
										
										
											2017-08-15 09:08:42 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-08-24 14:35:37 +08:00
										 |  |  | 		encoded, err := erasure.EncodeData(context.Background(), buffer) | 
					
						
							| 
									
										
										
										
											2017-08-15 09:08:42 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			t.Fatalf("Test %d: failed to encode data: %v", i, err) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-06-26 18:32:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-15 09:08:42 +08:00
										 |  |  | 		for j := range encoded[:test.missingData] { | 
					
						
							|  |  |  | 			encoded[j] = nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		for j := test.dataBlocks; j < test.dataBlocks+test.missingParity; j++ { | 
					
						
							|  |  |  | 			encoded[j] = nil | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-06-26 18:32:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-15 09:08:42 +08:00
										 |  |  | 		if test.reconstructParity { | 
					
						
							| 
									
										
										
										
											2018-08-24 14:35:37 +08:00
										 |  |  | 			err = erasure.DecodeDataAndParityBlocks(context.Background(), encoded) | 
					
						
							| 
									
										
										
										
											2017-08-15 09:08:42 +08:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2018-08-24 14:35:37 +08:00
										 |  |  | 			err = erasure.DecodeDataBlocks(encoded) | 
					
						
							| 
									
										
										
										
											2017-08-15 09:08:42 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-08-12 09:24:48 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-15 09:08:42 +08:00
										 |  |  | 		if err == nil && test.shouldFail { | 
					
						
							|  |  |  | 			t.Errorf("Test %d: test should fail but it passed", i) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if err != nil && !test.shouldFail { | 
					
						
							|  |  |  | 			t.Errorf("Test %d: test should pass but it failed: %v", i, err) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-08-12 09:24:48 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-15 09:08:42 +08:00
										 |  |  | 		decoded := encoded | 
					
						
							|  |  |  | 		if !test.shouldFail { | 
					
						
							|  |  |  | 			if test.reconstructParity { | 
					
						
							|  |  |  | 				for j := range decoded { | 
					
						
							|  |  |  | 					if decoded[j] == nil { | 
					
						
							|  |  |  | 						t.Errorf("Test %d: failed to reconstruct shard %d", i, j) | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2017-08-12 09:24:48 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2017-08-15 09:08:42 +08:00
										 |  |  | 			} else { | 
					
						
							|  |  |  | 				for j := range decoded[:test.dataBlocks] { | 
					
						
							|  |  |  | 					if decoded[j] == nil { | 
					
						
							|  |  |  | 						t.Errorf("Test %d: failed to reconstruct data shard %d", i, j) | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2017-08-12 09:24:48 +08:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2016-06-26 18:32:49 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-15 09:08:42 +08:00
										 |  |  | 			decodedData := new(bytes.Buffer) | 
					
						
							| 
									
										
										
										
											2018-04-06 06:04:40 +08:00
										 |  |  | 			if _, err = writeDataBlocks(context.Background(), decodedData, decoded, test.dataBlocks, 0, int64(len(data))); err != nil { | 
					
						
							| 
									
										
										
										
											2017-08-15 09:08:42 +08:00
										 |  |  | 				t.Errorf("Test %d: failed to write data blocks: %v", i, err) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if !bytes.Equal(decodedData.Bytes(), data) { | 
					
						
							|  |  |  | 				t.Errorf("Test %d: Decoded data does not match original data: got: %v want: %v", i, decodedData.Bytes(), data) | 
					
						
							| 
									
										
										
										
											2016-06-26 18:32:49 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-07-15 05:59:01 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-19 14:56:16 +08:00
										 |  |  | // Setup for erasureCreateFile and erasureReadFile tests.
 | 
					
						
							|  |  |  | type erasureTestSetup struct { | 
					
						
							|  |  |  | 	dataBlocks   int | 
					
						
							|  |  |  | 	parityBlocks int | 
					
						
							|  |  |  | 	blockSize    int64 | 
					
						
							|  |  |  | 	diskPaths    []string | 
					
						
							|  |  |  | 	disks        []StorageAPI | 
					
						
							| 
									
										
										
										
											2016-07-15 05:59:01 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-19 14:56:16 +08:00
										 |  |  | // Removes the temporary disk directories.
 | 
					
						
							|  |  |  | func (e erasureTestSetup) Remove() { | 
					
						
							|  |  |  | 	for _, path := range e.diskPaths { | 
					
						
							| 
									
										
										
										
											2017-08-13 10:25:43 +08:00
										 |  |  | 		os.RemoveAll(path) | 
					
						
							| 
									
										
										
										
											2016-07-19 14:56:16 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-07-15 05:59:01 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-19 14:56:16 +08:00
										 |  |  | // Returns an initialized setup for erasure tests.
 | 
					
						
							|  |  |  | func newErasureTestSetup(dataBlocks int, parityBlocks int, blockSize int64) (*erasureTestSetup, error) { | 
					
						
							| 
									
										
										
										
											2016-07-15 05:59:01 +08:00
										 |  |  | 	diskPaths := make([]string, dataBlocks+parityBlocks) | 
					
						
							|  |  |  | 	disks := make([]StorageAPI, len(diskPaths)) | 
					
						
							| 
									
										
										
										
											2016-07-30 16:26:19 +08:00
										 |  |  | 	var err error | 
					
						
							| 
									
										
										
										
											2016-07-15 05:59:01 +08:00
										 |  |  | 	for i := range diskPaths { | 
					
						
							| 
									
										
										
										
											2016-07-30 16:26:19 +08:00
										 |  |  | 		disks[i], diskPaths[i], err = newPosixTestSetup() | 
					
						
							| 
									
										
										
										
											2016-07-15 05:59:01 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2016-07-19 14:56:16 +08:00
										 |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2016-07-15 05:59:01 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		err = disks[i].MakeVol("testbucket") | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2016-07-19 14:56:16 +08:00
										 |  |  | 			return nil, err | 
					
						
							| 
									
										
										
										
											2016-07-15 05:59:01 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-07-19 14:56:16 +08:00
										 |  |  | 	return &erasureTestSetup{dataBlocks, parityBlocks, blockSize, diskPaths, disks}, nil | 
					
						
							| 
									
										
										
										
											2016-07-15 05:59:01 +08:00
										 |  |  | } |