/* * 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.fs.contract; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.LocatedFileStatus; import org.apache.hadoop.fs.Path; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicInteger; import org.apache.hadoop.fs.FileStatus; import org.apache.hadoop.test.LambdaTestUtils; import static org.apache.hadoop.fs.contract.ContractTestUtils.createFile; import static org.apache.hadoop.fs.contract.ContractTestUtils.dataset; import static org.apache.hadoop.fs.contract.ContractTestUtils.deleteChildren; import static org.apache.hadoop.fs.contract.ContractTestUtils.dumpStats; import static org.apache.hadoop.fs.contract.ContractTestUtils.listChildren; import static org.apache.hadoop.fs.contract.ContractTestUtils.toList; import static org.apache.hadoop.fs.contract.ContractTestUtils.treeWalk; /** * This class does things to the root directory. * Only subclass this for tests against transient filesystems where * you don't care about the data. */ public abstract class AbstractContractRootDirectoryTest extends AbstractFSContractTestBase { private static final Logger LOG = LoggerFactory.getLogger(AbstractContractRootDirectoryTest.class); public static final int OBJECTSTORE_RETRY_TIMEOUT = 30000; @Override public void setup() throws Exception { super.setup(); skipIfUnsupported(TEST_ROOT_TESTS_ENABLED); } @Test public void testMkDirDepth1() throws Throwable { FileSystem fs = getFileSystem(); Path dir = new Path("/testmkdirdepth1"); assertPathDoesNotExist("directory already exists", dir); fs.mkdirs(dir); assertIsDirectory(dir); assertPathExists("directory already exists", dir); assertDeleted(dir, true); } @Test public void testRmEmptyRootDirRecursive() throws Throwable { //extra sanity checks here to avoid support calls about complete loss of data skipIfUnsupported(TEST_ROOT_TESTS_ENABLED); Path root = new Path("/"); assertIsDirectory(root); boolean deleted = getFileSystem().delete(root, true); LOG.info("rm -r / of empty dir result is {}", deleted); assertIsDirectory(root); } @Test public void testRmEmptyRootDirNonRecursive() throws Throwable { // extra sanity checks here to avoid support calls about complete loss // of data skipIfUnsupported(TEST_ROOT_TESTS_ENABLED); final Path root = new Path("/"); assertIsDirectory(root); // make sure the directory is clean. This includes some retry logic // to forgive blobstores whose listings can be out of sync with the file // status; final FileSystem fs = getFileSystem(); final AtomicInteger iterations = new AtomicInteger(0); final FileStatus[] originalChildren = listChildren(fs, root); LambdaTestUtils.eventually( OBJECTSTORE_RETRY_TIMEOUT, new Callable<Void>() { @Override public Void call() throws Exception { FileStatus[] deleted = deleteChildren(fs, root, true); FileStatus[] children = listChildren(fs, root); if (children.length > 0) { fail(String.format( "After %d attempts: listing after rm /* not empty" + "\n%s\n%s\n%s", iterations.incrementAndGet(), dumpStats("final", children), dumpStats("deleted", deleted), dumpStats("original", originalChildren))); } return null; } }, new LambdaTestUtils.ProportionalRetryInterval(50, 1000)); // then try to delete the empty one boolean deleted = fs.delete(root, false); LOG.info("rm / of empty dir result is {}", deleted); assertIsDirectory(root); } @Test public void testRmNonEmptyRootDirNonRecursive() throws Throwable { //extra sanity checks here to avoid support calls about complete loss of data skipIfUnsupported(TEST_ROOT_TESTS_ENABLED); Path root = new Path("/"); String touchfile = "/testRmNonEmptyRootDirNonRecursive"; Path file = new Path(touchfile); ContractTestUtils.touch(getFileSystem(), file); assertIsDirectory(root); try { boolean deleted = getFileSystem().delete(root, false); fail("non recursive delete should have raised an exception," + " but completed with exit code " + deleted); } catch (IOException e) { //expected handleExpectedException(e); // and the file must still be present assertIsFile(file); } finally { getFileSystem().delete(file, false); } assertIsDirectory(root); } @Test public void testRmRootRecursive() throws Throwable { //extra sanity checks here to avoid support calls about complete loss of data skipIfUnsupported(TEST_ROOT_TESTS_ENABLED); Path root = new Path("/"); assertIsDirectory(root); Path file = new Path("/testRmRootRecursive"); ContractTestUtils.touch(getFileSystem(), file); boolean deleted = getFileSystem().delete(root, true); assertIsDirectory(root); LOG.info("rm -rf / result is {}", deleted); if (deleted) { assertPathDoesNotExist("expected file to be deleted", file); } else { assertPathExists("expected file to be preserved", file);; } } @Test public void testCreateFileOverRoot() throws Throwable { //extra sanity checks here to avoid support calls about complete loss of data skipIfUnsupported(TEST_ROOT_TESTS_ENABLED); Path root = new Path("/"); byte[] dataset = dataset(1024, ' ', 'z'); try { createFile(getFileSystem(), root, false, dataset); fail("expected an exception, got a file created over root: " + ls(root)); } catch (IOException e) { //expected handleExpectedException(e); } assertIsDirectory(root); } @Test public void testListEmptyRootDirectory() throws IOException { skipIfUnsupported(TEST_ROOT_TESTS_ENABLED); FileSystem fs = getFileSystem(); Path root = new Path("/"); FileStatus[] statuses = fs.listStatus(root); for (FileStatus status : statuses) { ContractTestUtils.assertDeleted(fs, status.getPath(), true); } assertEquals("listStatus on empty root-directory returned a non-empty list", 0, fs.listStatus(root).length); assertFalse("listFiles(/, false).hasNext", fs.listFiles(root, false).hasNext()); assertFalse("listFiles(/, true).hasNext", fs.listFiles(root, true).hasNext()); assertFalse("listLocatedStatus(/).hasNext", fs.listLocatedStatus(root).hasNext()); assertIsDirectory(root); } @Test public void testSimpleRootListing() throws IOException { describe("test the nonrecursive root listing calls"); FileSystem fs = getFileSystem(); Path root = new Path("/"); FileStatus[] statuses = fs.listStatus(root); List<LocatedFileStatus> locatedStatusList = toList( fs.listLocatedStatus(root)); assertEquals(statuses.length, locatedStatusList.size()); List<LocatedFileStatus> fileList = toList(fs.listFiles(root, false)); assertTrue(fileList.size() <= statuses.length); } @Test public void testRecursiveRootListing() throws IOException { describe("test a recursive root directory listing"); FileSystem fs = getFileSystem(); Path root = new Path("/"); ContractTestUtils.TreeScanResults listing = new ContractTestUtils.TreeScanResults( fs.listFiles(root, true)); describe("verifying consistency with treewalk's files"); ContractTestUtils.TreeScanResults treeWalk = treeWalk(fs, root); treeWalk.assertFieldsEquivalent("files", listing, treeWalk.getFiles(), listing.getFiles()); } }