/* * Minio Cloud Storage, (C) 2016 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" "crypto/rand" "os" "path" "testing" humanize "github.com/dustin/go-humanize" ) // Test erasureHealFile() func TestErasureHealFile(t *testing.T) { // Initialize environment needed for the test. dataBlocks := 7 parityBlocks := 7 blockSize := int64(blockSizeV1) setup, err := newErasureTestSetup(dataBlocks, parityBlocks, blockSize) if err != nil { t.Error(err) return } defer setup.Remove() disks := setup.disks // Prepare a slice of 1MiB with random data. data := make([]byte, 1*humanize.MiByte) _, err = rand.Read(data) if err != nil { t.Fatal(err) } // Create a test file. _, size, checkSums, err := erasureCreateFile(disks, "testbucket", "testobject1", bytes.NewReader(data), true, blockSize, dataBlocks, parityBlocks, bitRotAlgo, dataBlocks+1) if err != nil { t.Fatal(err) } if size != int64(len(data)) { t.Errorf("erasureCreateFile returned %d, expected %d", size, len(data)) } latest := make([]StorageAPI, len(disks)) // Slice of latest disks outDated := make([]StorageAPI, len(disks)) // Slice of outdated disks // Test case when one part needs to be healed. dataPath := path.Join(setup.diskPaths[0], "testbucket", "testobject1") err = os.Remove(dataPath) if err != nil { t.Fatal(err) } copy(latest, disks) latest[0] = nil outDated[0] = disks[0] healCheckSums, err := erasureHealFile(latest, outDated, "testbucket", "testobject1", "testbucket", "testobject1", 1*humanize.MiByte, blockSize, dataBlocks, parityBlocks, bitRotAlgo) if err != nil { t.Fatal(err) } // Checksum of the healed file should match. if checkSums[0] != healCheckSums[0] { t.Error("Healing failed, data does not match.") } // Test case when parityBlocks number of disks need to be healed. // Should succeed. copy(latest, disks) for index := 0; index < parityBlocks; index++ { dataPath := path.Join(setup.diskPaths[index], "testbucket", "testobject1") err = os.Remove(dataPath) if err != nil { t.Fatal(err) } latest[index] = nil outDated[index] = disks[index] } healCheckSums, err = erasureHealFile(latest, outDated, "testbucket", "testobject1", "testbucket", "testobject1", 1*humanize.MiByte, blockSize, dataBlocks, parityBlocks, bitRotAlgo) if err != nil { t.Fatal(err) } // Checksums of the healed files should match. for index := 0; index < parityBlocks; index++ { if checkSums[index] != healCheckSums[index] { t.Error("Healing failed, data does not match.") } } for index := dataBlocks; index < len(disks); index++ { if healCheckSums[index] != "" { t.Errorf("expected healCheckSums[%d] to be empty", index) } } // Test case when parityBlocks+1 number of disks need to be healed. // Should fail. copy(latest, disks) for index := 0; index < parityBlocks+1; index++ { dataPath := path.Join(setup.diskPaths[index], "testbucket", "testobject1") err = os.Remove(dataPath) if err != nil { t.Fatal(err) } latest[index] = nil outDated[index] = disks[index] } _, err = erasureHealFile(latest, outDated, "testbucket", "testobject1", "testbucket", "testobject1", 1*humanize.MiByte, blockSize, dataBlocks, parityBlocks, bitRotAlgo) if err == nil { t.Error("Expected erasureHealFile() to fail when the number of available disks <= parityBlocks") } }