|
@@ -0,0 +1,204 @@
|
|
|
+/**
|
|
|
+ * Licensed to the Apache Software Foundation (ASF) under one
|
|
|
+ * or more contributor license agreements. See the NOTICE file
|
|
|
+ * distributed with this work for additional information
|
|
|
+ * regarding copyright ownership. The ASF licenses this file
|
|
|
+ * to you 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 org.apache.hadoop.tools.contract;
|
|
|
+
|
|
|
+import static org.apache.hadoop.fs.contract.ContractTestUtils.*;
|
|
|
+import static org.junit.Assert.*;
|
|
|
+
|
|
|
+import java.util.Arrays;
|
|
|
+
|
|
|
+import org.apache.hadoop.conf.Configuration;
|
|
|
+import org.apache.hadoop.fs.FileSystem;
|
|
|
+import org.apache.hadoop.fs.Path;
|
|
|
+import org.apache.hadoop.fs.contract.AbstractFSContractTestBase;
|
|
|
+import org.apache.hadoop.fs.contract.ContractTestUtils;
|
|
|
+import org.apache.hadoop.mapreduce.Job;
|
|
|
+import org.apache.hadoop.test.GenericTestUtils;
|
|
|
+import org.apache.hadoop.tools.DistCp;
|
|
|
+import org.apache.hadoop.tools.DistCpOptions;
|
|
|
+
|
|
|
+import org.junit.Before;
|
|
|
+import org.junit.Rule;
|
|
|
+import org.junit.Test;
|
|
|
+import org.junit.rules.TestName;
|
|
|
+
|
|
|
+/**
|
|
|
+ * Contract test suite covering a file system's integration with DistCp. The
|
|
|
+ * tests coordinate two file system instances: one "local", which is the local
|
|
|
+ * file system, and the other "remote", which is the file system implementation
|
|
|
+ * under test. The tests in the suite cover both copying from local to remote
|
|
|
+ * (e.g. a backup use case) and copying from remote to local (e.g. a restore use
|
|
|
+ * case).
|
|
|
+ */
|
|
|
+public abstract class AbstractContractDistCpTest
|
|
|
+ extends AbstractFSContractTestBase {
|
|
|
+
|
|
|
+ @Rule
|
|
|
+ public TestName testName = new TestName();
|
|
|
+
|
|
|
+ private Configuration conf;
|
|
|
+ private FileSystem localFS, remoteFS;
|
|
|
+ private Path localDir, remoteDir;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ protected Configuration createConfiguration() {
|
|
|
+ Configuration newConf = new Configuration();
|
|
|
+ newConf.set("mapred.job.tracker", "local");
|
|
|
+ return newConf;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Before
|
|
|
+ @Override
|
|
|
+ public void setup() throws Exception {
|
|
|
+ super.setup();
|
|
|
+ conf = getContract().getConf();
|
|
|
+ localFS = FileSystem.getLocal(conf);
|
|
|
+ remoteFS = getFileSystem();
|
|
|
+ // Test paths are isolated by concrete subclass name and test method name.
|
|
|
+ // All paths are fully qualified including scheme (not taking advantage of
|
|
|
+ // default file system), so if something fails, the messages will make it
|
|
|
+ // clear which paths are local and which paths are remote.
|
|
|
+ Path testSubDir = new Path(getClass().getSimpleName(),
|
|
|
+ testName.getMethodName());
|
|
|
+ localDir = localFS.makeQualified(new Path(new Path(
|
|
|
+ GenericTestUtils.getTestDir().toURI()), testSubDir));
|
|
|
+ mkdirs(localFS, localDir);
|
|
|
+ remoteDir = remoteFS.makeQualified(
|
|
|
+ new Path(getContract().getTestPath(), testSubDir));
|
|
|
+ mkdirs(remoteFS, remoteDir);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void deepDirectoryStructureToRemote() throws Exception {
|
|
|
+ describe("copy a deep directory structure from local to remote");
|
|
|
+ deepDirectoryStructure(localFS, localDir, remoteFS, remoteDir);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void largeFilesToRemote() throws Exception {
|
|
|
+ describe("copy multiple large files from local to remote");
|
|
|
+ largeFiles(localFS, localDir, remoteFS, remoteDir);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void deepDirectoryStructureFromRemote() throws Exception {
|
|
|
+ describe("copy a deep directory structure from remote to local");
|
|
|
+ deepDirectoryStructure(remoteFS, remoteDir, localFS, localDir);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Test
|
|
|
+ public void largeFilesFromRemote() throws Exception {
|
|
|
+ describe("copy multiple large files from remote to local");
|
|
|
+ largeFiles(remoteFS, remoteDir, localFS, localDir);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Executes a test using a file system sub-tree with multiple nesting levels.
|
|
|
+ *
|
|
|
+ * @param srcFS source FileSystem
|
|
|
+ * @param srcDir source directory
|
|
|
+ * @param dstFS destination FileSystem
|
|
|
+ * @param dstDir destination directory
|
|
|
+ * @throws Exception if there is a failure
|
|
|
+ */
|
|
|
+ private void deepDirectoryStructure(FileSystem srcFS, Path srcDir,
|
|
|
+ FileSystem dstFS, Path dstDir) throws Exception {
|
|
|
+ Path inputDir = new Path(srcDir, "inputDir");
|
|
|
+ Path inputSubDir1 = new Path(inputDir, "subDir1");
|
|
|
+ Path inputSubDir2 = new Path(inputDir, "subDir2/subDir3");
|
|
|
+ Path inputFile1 = new Path(inputDir, "file1");
|
|
|
+ Path inputFile2 = new Path(inputSubDir1, "file2");
|
|
|
+ Path inputFile3 = new Path(inputSubDir2, "file3");
|
|
|
+ mkdirs(srcFS, inputSubDir1);
|
|
|
+ mkdirs(srcFS, inputSubDir2);
|
|
|
+ byte[] data1 = dataset(100, 33, 43);
|
|
|
+ createFile(srcFS, inputFile1, true, data1);
|
|
|
+ byte[] data2 = dataset(200, 43, 53);
|
|
|
+ createFile(srcFS, inputFile2, true, data2);
|
|
|
+ byte[] data3 = dataset(300, 53, 63);
|
|
|
+ createFile(srcFS, inputFile3, true, data3);
|
|
|
+ Path target = new Path(dstDir, "outputDir");
|
|
|
+ runDistCp(inputDir, target);
|
|
|
+ ContractTestUtils.assertIsDirectory(dstFS, target);
|
|
|
+ verifyFileContents(dstFS, new Path(target, "inputDir/file1"), data1);
|
|
|
+ verifyFileContents(dstFS,
|
|
|
+ new Path(target, "inputDir/subDir1/file2"), data2);
|
|
|
+ verifyFileContents(dstFS,
|
|
|
+ new Path(target, "inputDir/subDir2/subDir3/file3"), data3);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Executes a test using multiple large files.
|
|
|
+ *
|
|
|
+ * @param srcFS source FileSystem
|
|
|
+ * @param srcDir source directory
|
|
|
+ * @param dstFS destination FileSystem
|
|
|
+ * @param dstDir destination directory
|
|
|
+ * @throws Exception if there is a failure
|
|
|
+ */
|
|
|
+ private void largeFiles(FileSystem srcFS, Path srcDir, FileSystem dstFS,
|
|
|
+ Path dstDir) throws Exception {
|
|
|
+ Path inputDir = new Path(srcDir, "inputDir");
|
|
|
+ Path inputFile1 = new Path(inputDir, "file1");
|
|
|
+ Path inputFile2 = new Path(inputDir, "file2");
|
|
|
+ Path inputFile3 = new Path(inputDir, "file3");
|
|
|
+ mkdirs(srcFS, inputDir);
|
|
|
+ int fileSizeKb = conf.getInt("scale.test.distcp.file.size.kb", 10 * 1024);
|
|
|
+ int fileSizeMb = fileSizeKb * 1024;
|
|
|
+ getLog().info("{} with file size {}", testName.getMethodName(), fileSizeMb);
|
|
|
+ byte[] data1 = dataset((fileSizeMb + 1) * 1024 * 1024, 33, 43);
|
|
|
+ createFile(srcFS, inputFile1, true, data1);
|
|
|
+ byte[] data2 = dataset((fileSizeMb + 2) * 1024 * 1024, 43, 53);
|
|
|
+ createFile(srcFS, inputFile2, true, data2);
|
|
|
+ byte[] data3 = dataset((fileSizeMb + 3) * 1024 * 1024, 53, 63);
|
|
|
+ createFile(srcFS, inputFile3, true, data3);
|
|
|
+ Path target = new Path(dstDir, "outputDir");
|
|
|
+ runDistCp(inputDir, target);
|
|
|
+ ContractTestUtils.assertIsDirectory(dstFS, target);
|
|
|
+ verifyFileContents(dstFS, new Path(target, "inputDir/file1"), data1);
|
|
|
+ verifyFileContents(dstFS, new Path(target, "inputDir/file2"), data2);
|
|
|
+ verifyFileContents(dstFS, new Path(target, "inputDir/file3"), data3);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Executes DistCp and asserts that the job finished successfully.
|
|
|
+ *
|
|
|
+ * @param src source path
|
|
|
+ * @param dst destination path
|
|
|
+ * @throws Exception if there is a failure
|
|
|
+ */
|
|
|
+ private void runDistCp(Path src, Path dst) throws Exception {
|
|
|
+ DistCpOptions options = new DistCpOptions(Arrays.asList(src), dst);
|
|
|
+ Job job = new DistCp(conf, options).execute();
|
|
|
+ assertNotNull("Unexpected null job returned from DistCp execution.", job);
|
|
|
+ assertTrue("DistCp job did not complete.", job.isComplete());
|
|
|
+ assertTrue("DistCp job did not complete successfully.", job.isSuccessful());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Creates a directory and any ancestor directories required.
|
|
|
+ *
|
|
|
+ * @param fs FileSystem in which to create directories
|
|
|
+ * @param dir path of directory to create
|
|
|
+ * @throws Exception if there is a failure
|
|
|
+ */
|
|
|
+ private static void mkdirs(FileSystem fs, Path dir) throws Exception {
|
|
|
+ assertTrue("Failed to mkdir " + dir, fs.mkdirs(dir));
|
|
|
+ }
|
|
|
+}
|