diff --git a/hadoop-common-project/hadoop-common/CHANGES.txt b/hadoop-common-project/hadoop-common/CHANGES.txt index 830ffa9ae2e..36c441ba02b 100644 --- a/hadoop-common-project/hadoop-common/CHANGES.txt +++ b/hadoop-common-project/hadoop-common/CHANGES.txt @@ -684,6 +684,8 @@ Release 0.23.3 - UNRELEASED HADOOP-8550. hadoop fs -touchz automatically created parent directories (John George via bobby) + HADOOP-8635. Cannot cancel paths registered deleteOnExit (daryn via bobby) + Release 0.23.2 - UNRELEASED NEW FEATURES diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FileSystem.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FileSystem.java index 42b30c4999b..2fa4bd0c038 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FileSystem.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FileSystem.java @@ -1214,6 +1214,16 @@ public boolean deleteOnExit(Path f) throws IOException { } return true; } + + /** + * Cancel the deletion of the path when the FileSystem is closed + * @param f the path to cancel deletion + */ + public boolean cancelDeleteOnExit(Path f) { + synchronized (deleteOnExit) { + return deleteOnExit.remove(f); + } + } /** * Delete all files that were marked as delete-on-exit. This recursively diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/TestFileSystemCaching.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/TestFileSystemCaching.java index e8debf8dfaf..7e5f99f5fb3 100644 --- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/TestFileSystemCaching.java +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/TestFileSystemCaching.java @@ -269,26 +269,82 @@ public FileSystem run() throws Exception { } @Test - public void testDeleteOnExitChecksExists() throws Exception { + public void testDelete() throws IOException { + FileSystem mockFs = mock(FileSystem.class); + FileSystem fs = new FilterFileSystem(mockFs); + Path path = new Path("/a"); + + fs.delete(path, false); + verify(mockFs).delete(eq(path), eq(false)); + reset(mockFs); + fs.delete(path, true); + verify(mockFs).delete(eq(path), eq(true)); + } + + @Test + public void testDeleteOnExit() throws IOException { FileSystem mockFs = mock(FileSystem.class); FileSystem fs = new FilterFileSystem(mockFs); - Path p = new Path("/a"); - - // path has to exist for deleteOnExit to register it - when(mockFs.getFileStatus(p)).thenReturn(new FileStatus()); - fs.deleteOnExit(p); - verify(mockFs).getFileStatus(eq(p)); - fs.close(); - verify(mockFs).delete(eq(p), anyBoolean()); + Path path = new Path("/a"); + + // delete on close if path does exist + when(mockFs.getFileStatus(eq(path))).thenReturn(new FileStatus()); + assertTrue(fs.deleteOnExit(path)); + verify(mockFs).getFileStatus(eq(path)); reset(mockFs); - - // make sure it doesn't try to delete a file that doesn't exist - when(mockFs.getFileStatus(p)).thenReturn(new FileStatus()); - fs.deleteOnExit(p); - verify(mockFs).getFileStatus(eq(p)); + when(mockFs.getFileStatus(eq(path))).thenReturn(new FileStatus()); + fs.close(); + verify(mockFs).getFileStatus(eq(path)); + verify(mockFs).delete(eq(path), eq(true)); + } + + @Test + public void testDeleteOnExitFNF() throws IOException { + FileSystem mockFs = mock(FileSystem.class); + FileSystem fs = new FilterFileSystem(mockFs); + Path path = new Path("/a"); + + // don't delete on close if path doesn't exist + assertFalse(fs.deleteOnExit(path)); + verify(mockFs).getFileStatus(eq(path)); reset(mockFs); fs.close(); - verify(mockFs).getFileStatus(eq(p)); + verify(mockFs, never()).getFileStatus(eq(path)); verify(mockFs, never()).delete(any(Path.class), anyBoolean()); } -} \ No newline at end of file + + + @Test + public void testDeleteOnExitRemoved() throws IOException { + FileSystem mockFs = mock(FileSystem.class); + FileSystem fs = new FilterFileSystem(mockFs); + Path path = new Path("/a"); + + // don't delete on close if path existed, but later removed + when(mockFs.getFileStatus(eq(path))).thenReturn(new FileStatus()); + assertTrue(fs.deleteOnExit(path)); + verify(mockFs).getFileStatus(eq(path)); + reset(mockFs); + fs.close(); + verify(mockFs).getFileStatus(eq(path)); + verify(mockFs, never()).delete(any(Path.class), anyBoolean()); + } + + @Test + public void testCancelDeleteOnExit() throws IOException { + FileSystem mockFs = mock(FileSystem.class); + FileSystem fs = new FilterFileSystem(mockFs); + Path path = new Path("/a"); + + // don't delete on close if path existed, but later cancelled + when(mockFs.getFileStatus(eq(path))).thenReturn(new FileStatus()); + assertTrue(fs.deleteOnExit(path)); + verify(mockFs).getFileStatus(eq(path)); + assertTrue(fs.cancelDeleteOnExit(path)); + assertFalse(fs.cancelDeleteOnExit(path)); // false because not registered + reset(mockFs); + fs.close(); + verify(mockFs, never()).getFileStatus(any(Path.class)); + verify(mockFs, never()).delete(any(Path.class), anyBoolean()); + } +} diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/TestFilterFileSystem.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/TestFilterFileSystem.java index a374c08a186..727986dcd12 100644 --- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/TestFilterFileSystem.java +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/TestFilterFileSystem.java @@ -182,6 +182,9 @@ public Token getDelegationToken(String renewer) throws IOException { public boolean deleteOnExit(Path f) throws IOException { return false; } + public boolean cancelDeleteOnExit(Path f) throws IOException { + return false; + } public String getScheme() { return "dontcheck"; }