-
Notifications
You must be signed in to change notification settings - Fork 9.1k
HADOOP-14630 Contract Tests to verify create, mkdirs and rename under a file is forbidden #533
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d658d1a
336f657
8308cb6
33f3ca2
ea6940b
fac64e5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -477,11 +477,11 @@ running out of memory as it calculates the partitions. | |
|
|
||
| Any FileSystem that does not actually break files into blocks SHOULD | ||
| return a number for this that results in efficient processing. | ||
| A FileSystem MAY make this user-configurable (the S3 and Swift filesystem clients do this). | ||
| A FileSystem MAY make this user-configurable (the object store connectors usually do this). | ||
|
|
||
| ### `long getDefaultBlockSize(Path p)` | ||
|
|
||
| Get the "default" block size for a path —that is, the block size to be used | ||
| Get the "default" block size for a path --that is, the block size to be used | ||
| when writing objects to a path in the filesystem. | ||
|
|
||
| #### Preconditions | ||
|
|
@@ -530,14 +530,21 @@ on the filesystem. | |
|
|
||
| ### `boolean mkdirs(Path p, FsPermission permission)` | ||
|
|
||
| Create a directory and all its parents | ||
| Create a directory and all its parents. | ||
|
|
||
| #### Preconditions | ||
|
|
||
|
|
||
| The path must either be a directory or not exist | ||
|
|
||
|
||
| if exists(FS, p) and not isDir(FS, p) : | ||
| raise [ParentNotDirectoryException, FileAlreadyExistsException, IOException] | ||
|
|
||
| No ancestor may be a file | ||
|
|
||
| forall d = ancestors(FS, p) : | ||
|
||
| if exists(FS, d) and not isDir(FS, d) : | ||
| raise [ParentNotDirectoryException, FileAlreadyExistsException, IOException] | ||
|
|
||
| #### Postconditions | ||
|
|
||
|
|
@@ -577,14 +584,20 @@ Writing to or overwriting a directory must fail. | |
|
|
||
| if isDir(FS, p) : raise {FileAlreadyExistsException, FileNotFoundException, IOException} | ||
|
|
||
| No ancestor may be a file | ||
|
|
||
| forall d = ancestors(FS, p) : | ||
|
||
| if exists(FS, d) and not isDir(FS, d) : | ||
| raise [ParentNotDirectoryException, FileAlreadyExistsException, IOException] | ||
|
|
||
| FileSystems may reject the request for other | ||
| reasons, such as the FS being read-only (HDFS), | ||
| the block size being below the minimum permitted (HDFS), | ||
| the replication count being out of range (HDFS), | ||
| quotas on namespace or filesystem being exceeded, reserved | ||
| names, etc. All rejections SHOULD be `IOException` or a subclass thereof | ||
| and MAY be a `RuntimeException` or subclass. For instance, HDFS may raise a `InvalidPathException`. | ||
| and MAY be a `RuntimeException` or subclass. | ||
| For instance, HDFS may raise an `InvalidPathException`. | ||
|
|
||
| #### Postconditions | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,11 +22,11 @@ | |
| import org.apache.hadoop.fs.FileAlreadyExistsException; | ||
| import org.apache.hadoop.fs.FileStatus; | ||
| import org.apache.hadoop.fs.FileSystem; | ||
| import org.apache.hadoop.fs.ParentNotDirectoryException; | ||
| import org.apache.hadoop.fs.Path; | ||
| import org.junit.Test; | ||
| import org.junit.internal.AssumptionViolatedException; | ||
| import org.junit.AssumptionViolatedException; | ||
|
|
||
| import java.io.FileNotFoundException; | ||
| import java.io.IOException; | ||
|
|
||
| import static org.apache.hadoop.fs.contract.ContractTestUtils.dataset; | ||
|
|
@@ -40,7 +40,7 @@ | |
| * Test creating files, overwrite options etc. | ||
| */ | ||
| public abstract class AbstractContractCreateTest extends | ||
| AbstractFSContractTestBase { | ||
| AbstractFSContractTestBase { | ||
|
|
||
| /** | ||
| * How long to wait for a path to become visible. | ||
|
|
@@ -113,7 +113,6 @@ private void testOverwriteExistingFile(boolean useBuilder) throws Throwable { | |
| * This test catches some eventual consistency problems that blobstores exhibit, | ||
| * as we are implicitly verifying that updates are consistent. This | ||
| * is why different file lengths and datasets are used | ||
| * @throws Throwable | ||
| */ | ||
| @Test | ||
| public void testOverwriteExistingFile() throws Throwable { | ||
|
|
@@ -137,10 +136,6 @@ private void testOverwriteEmptyDirectory(boolean useBuilder) | |
| } catch (FileAlreadyExistsException expected) { | ||
| //expected | ||
| handleExpectedException(expected); | ||
| } catch (FileNotFoundException e) { | ||
| handleRelaxedException("overwriting a dir with a file ", | ||
| "FileAlreadyExistsException", | ||
| e); | ||
| } catch (IOException e) { | ||
| handleRelaxedException("overwriting a dir with a file ", | ||
| "FileAlreadyExistsException", | ||
|
|
@@ -189,10 +184,6 @@ private void testOverwriteNonEmptyDirectory(boolean useBuilder) | |
| } catch (FileAlreadyExistsException expected) { | ||
| //expected | ||
| handleExpectedException(expected); | ||
| } catch (FileNotFoundException e) { | ||
| handleRelaxedException("overwriting a dir with a file ", | ||
| "FileAlreadyExistsException", | ||
| e); | ||
| } catch (IOException e) { | ||
| handleRelaxedException("overwriting a dir with a file ", | ||
| "FileAlreadyExistsException", | ||
|
|
@@ -332,4 +323,117 @@ public void testCreateMakesParentDirs() throws Throwable { | |
| assertTrue("Grandparent directory does not appear to be a directory", | ||
| fs.getFileStatus(grandparent).isDirectory()); | ||
| } | ||
|
|
||
| @Test | ||
| public void testCreateFileUnderFile() throws Throwable { | ||
| describe("Verify that it is forbidden to create file/file"); | ||
| if (isSupported(CREATE_FILE_UNDER_FILE_ALLOWED)) { | ||
| // object store or some file systems: downgrade to a skip so that the | ||
| // failure is visible in test results | ||
| skip("This filesystem supports creating files under files"); | ||
| } | ||
| Path grandparent = methodPath(); | ||
| Path parent = new Path(grandparent, "parent"); | ||
| expectCreateUnderFileFails( | ||
| "creating a file under a file", | ||
| grandparent, | ||
| parent); | ||
| } | ||
|
|
||
| @Test | ||
| public void testCreateUnderFileSubdir() throws Throwable { | ||
| describe("Verify that it is forbidden to create file/dir/file"); | ||
| if (isSupported(CREATE_FILE_UNDER_FILE_ALLOWED)) { | ||
| // object store or some file systems: downgrade to a skip so that the | ||
| // failure is visible in test results | ||
| skip("This filesystem supports creating files under files"); | ||
| } | ||
| Path grandparent = methodPath(); | ||
| Path parent = new Path(grandparent, "parent"); | ||
| Path child = new Path(parent, "child"); | ||
| expectCreateUnderFileFails( | ||
| "creating a file under a subdirectory of a file", | ||
| grandparent, | ||
| child); | ||
| } | ||
|
|
||
|
|
||
| @Test | ||
| public void testMkdirUnderFile() throws Throwable { | ||
| describe("Verify that it is forbidden to create file/dir"); | ||
| Path grandparent = methodPath(); | ||
| Path parent = new Path(grandparent, "parent"); | ||
| expectMkdirsUnderFileFails("mkdirs() under a file", | ||
| grandparent, parent); | ||
| } | ||
|
|
||
| @Test | ||
| public void testMkdirUnderFileSubdir() throws Throwable { | ||
| describe("Verify that it is forbidden to create file/dir/dir"); | ||
| Path grandparent = methodPath(); | ||
| Path parent = new Path(grandparent, "parent"); | ||
| Path child = new Path(parent, "child"); | ||
| expectMkdirsUnderFileFails("mkdirs() file/dir", | ||
| grandparent, child); | ||
|
|
||
| try { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need another Is this for verbose logging output? I mean, v.s. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just being curious about deeper creation .. those object stores are so troublesome here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes I think this test |
||
| // create the child | ||
| mkdirs(child); | ||
| } catch (FileAlreadyExistsException | ParentNotDirectoryException ex) { | ||
| // either of these may be raised. | ||
| handleExpectedException(ex); | ||
| } catch (IOException e) { | ||
| handleRelaxedException("creating a file under a subdirectory of a file ", | ||
| "FileAlreadyExistsException", | ||
| e); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Expect that touch() will fail because the parent is a file. | ||
| * @param action action for message | ||
| * @param file filename to create | ||
| * @param descendant path under file | ||
| * @throws Exception failure | ||
| */ | ||
| protected void expectCreateUnderFileFails(String action, | ||
| Path file, Path descendant) | ||
| throws Exception { | ||
| createFile(file); | ||
| try { | ||
| // create the child | ||
| createFile(descendant); | ||
| } catch (FileAlreadyExistsException | ParentNotDirectoryException ex) { | ||
| //expected | ||
| handleExpectedException(ex); | ||
| } catch (IOException e) { | ||
| handleRelaxedException(action, | ||
| "ParentNotDirectoryException", | ||
| e); | ||
| } | ||
| } | ||
|
|
||
| protected void expectMkdirsUnderFileFails(String action, | ||
| Path file, Path descendant) | ||
| throws Exception { | ||
| createFile(file); | ||
| try { | ||
| // now mkdirs | ||
| mkdirs(descendant); | ||
| } catch (FileAlreadyExistsException | ParentNotDirectoryException ex) { | ||
| //expected | ||
| handleExpectedException(ex); | ||
| } catch (IOException e) { | ||
| handleRelaxedException(action, | ||
| "ParentNotDirectoryException", | ||
| e); | ||
| } | ||
| } | ||
|
|
||
| private void createFile(Path path) throws IOException { | ||
| byte[] data = dataset(256, 'a', 'z'); | ||
| FileSystem fs = getFileSystem(); | ||
| writeDataset(fs, path, data, data.length, 1024 * 1024, | ||
| true); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
whitespace:end of line