[
https://issues.apache.org/jira/browse/HADOOP-18193?focusedWorklogId=768627&page=com.atlassian.jira.plugin.system.issuetabpanels:worklog-tabpanel#worklog-768627
]
ASF GitHub Bot logged work on HADOOP-18193:
-------------------------------------------
Author: ASF GitHub Bot
Created on: 10/May/22 18:07
Start Date: 10/May/22 18:07
Worklog Time Spent: 10m
Work Description: li-leyang commented on code in PR #4181:
URL: https://github.com/apache/hadoop/pull/4181#discussion_r852343467
##########
hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/viewfs/TestNestedMountPoint.java:
##########
@@ -0,0 +1,345 @@
+package org.apache.hadoop.fs.viewfs;
+
+import java.net.URI;
+import java.util.List;
+import java.util.function.Function;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.FsConstants;
+import org.apache.hadoop.fs.Path;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+
+public class TestNestedMountPoint {
+ private InodeTree inodeTree;
+ private Configuration conf;
+ private String mtName;
+ private URI fsUri;
+
+ static class TestNestMountPointFileSystem {
+ public URI getUri() {
+ return uri;
+ }
+
+ private URI uri;
+
+ TestNestMountPointFileSystem(URI uri) {
+ this.uri = uri;
+ }
+ }
+
+ static class TestNestMountPointInternalFileSystem extends
TestNestMountPointFileSystem {
+ TestNestMountPointInternalFileSystem(URI uri) {
+ super(uri);
+ }
+ }
+
+ private static final URI LINKFALLBACK_TARGET = URI.create("hdfs://nn00");
+ private static final URI NN1_TARGET = URI.create("hdfs://nn01/a/b");
+ private static final URI NN2_TARGET = URI.create("hdfs://nn02/a/b/e");
+ private static final URI NN3_TARGET = URI.create("hdfs://nn03/a/b/c/d");
+ private static final URI NN4_TARGET = URI.create("hdfs://nn04/a/b/c/d/e");
+ private static final URI NN5_TARGET = URI.create("hdfs://nn05/b/c/d/e");
+ private static final URI NN6_TARGET = URI.create("hdfs://nn06/b/c/d/e/f");
+
+ @Before
+ public void setUp() throws Exception {
+ conf = new Configuration();
+ mtName = TestNestedMountPoint.class.getName();
+ ConfigUtil.setIsNestedMountPointSupported(conf, true);
+ ConfigUtil.addLink(conf, mtName, "/a/b", NN1_TARGET);
+ ConfigUtil.addLink(conf, mtName, "/a/b/e", NN2_TARGET);
Review Comment:
Not sure what test case you are referring to
/a/b is nested in /a/b/c/d which is nested in /a/b/c/d/e.
Do you mean to add the test case like below:
/a/b -> /a/b/c/d ->/a/b/c/d/e
/a/b -> /a/b/e -> /a/b/e/f
(/a/b is a dirlink has has two nested mount points)
##########
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/viewfs/ConfigUtil.java:
##########
@@ -247,4 +247,22 @@ public static String getDefaultMountTableName(final
Configuration conf) {
return conf.get(Constants.CONFIG_VIEWFS_DEFAULT_MOUNT_TABLE_NAME_KEY,
Constants.CONFIG_VIEWFS_DEFAULT_MOUNT_TABLE);
}
+
+ /**
+ * Get the bool config whether nested mount point is supported.
Review Comment:
this is to get config value from conf object. I will change it to "check".
##########
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/viewfs/InodeTree.java:
##########
@@ -133,6 +138,9 @@ public INode(String pathToNode, UserGroupInformation aUgi) {
// and is read only.
abstract boolean isInternalDir();
+ // Identity some type of inodes(nested mount point).
Review Comment:
yeah, will expand this comment
##########
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/viewfs/InodeTree.java:
##########
@@ -212,6 +225,33 @@ void addLink(final String pathComponent, final
INodeLink<T> link)
}
children.put(pathComponent, link);
}
+
+ void addDirLink(final String pathComponent, final INodeDirLink<T> dirLink)
{
+ children.put(pathComponent, dirLink);
+ }
+ }
+
+ /**
+ * Internal class to represent nested mount points in INodeTree. Nested
mount points are non-leaf nodes in INode tree.
Review Comment:
I have added in the comments.
##########
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/viewfs/InodeTree.java:
##########
@@ -682,6 +736,25 @@ protected InodeTree(final Configuration config, final
String viewName,
}
}
+ private Collection<LinkEntry> getLinkEntries(List<LinkEntry> linkEntries) {
+ if (isNestedMountPointSupported) {
Review Comment:
The main reason for sorting is to group nested mp together and create
INodeDirLink for nested mp:
Lets say we have 3 mount points sorted:
/foo, /foo/bar and /foo/bar/baz
When creating inode tree:
1st pass: /foo -> create INodeLink for /foo
2nd pass: /foo/bar -> /foo is already a INodeLink so /foo is nested,
updating /foo as INodeDirLink, creating /foo/bar as INodeLink
3nd pass: /foo/bar/baz -> /foo/bar is INodeLink so/foo/bar is also nested,
updating /foo/bar to INodeDirLink, creating /foo/bar/baz as INodeLink.
##########
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/viewfs/InodeTree.java:
##########
@@ -212,6 +225,33 @@ void addLink(final String pathComponent, final
INodeLink<T> link)
}
children.put(pathComponent, link);
}
+
+ void addDirLink(final String pathComponent, final INodeDirLink<T> dirLink)
{
+ children.put(pathComponent, dirLink);
+ }
+ }
+
+ /**
+ * Internal class to represent nested mount points in INodeTree. Nested
mount points are non-leaf nodes in INode tree.
+ * Hence it inherits INodeDir. But it has additional attributes so wrap it
with link.
+ * @param <T>
+ */
+ static class INodeDirLink<T> extends INodeDir<T> {
Review Comment:
I have added nested mp semantics in the comment
##########
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/viewfs/InodeTree.java:
##########
@@ -212,6 +225,33 @@ void addLink(final String pathComponent, final
INodeLink<T> link)
}
children.put(pathComponent, link);
}
+
+ void addDirLink(final String pathComponent, final INodeDirLink<T> dirLink)
{
+ children.put(pathComponent, dirLink);
+ }
+ }
+
+ /**
+ * Internal class to represent nested mount points in INodeTree. Nested
mount points are non-leaf nodes in INode tree.
+ * Hence it inherits INodeDir. But it has additional attributes so wrap it
with link.
+ * @param <T>
+ */
+ static class INodeDirLink<T> extends INodeDir<T> {
Review Comment:
The class inheritance hierarchy at high level is:
INode:
Base class
INodeDir -> INode
Representing internal dir to mount point.
isLInk(): false
isInternalDir(): true
**isDirAndLink(): false**
INodeLink -> INode
Representing non-nested mount point
IsLInk(): true
isInternalDir(): false
**isDirAndLink(): false**
**INodeDirLink -> INodeDir
Representing nested mount point
isLink(): false
isInternalDir(): true
isDirAndLink(): true**
Highlighted are newly added in the PR.
##########
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/viewfs/InodeTree.java:
##########
@@ -682,6 +748,32 @@ protected InodeTree(final Configuration config, final
String viewName,
}
}
+ /**
+ * Get collection of linkEntry. If nested mount point is supported, sort
mount point src path based on alphabetical order.
Review Comment:
Fixed
##########
hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/viewfs/ViewFileSystemBaseTest.java:
##########
@@ -662,177 +790,177 @@ public void testResolvePathMountPoints() throws
IOException {
fsView.resolvePath(new Path("/internalDir/internalDir2/linkToDir3")));
}
-
+
@Test
public void testResolvePathThroughMountPoints() throws IOException {
fileSystemTestHelper.createFile(fsView, "/user/foo");
Assert.assertEquals(new Path(targetTestRoot,"user/foo"),
fsView.resolvePath(new Path("/user/foo")));
-
+
fsView.mkdirs(
fileSystemTestHelper.getTestRootPath(fsView, "/user/dirX"));
Assert.assertEquals(new Path(targetTestRoot,"user/dirX"),
fsView.resolvePath(new Path("/user/dirX")));
-
+
fsView.mkdirs(
fileSystemTestHelper.getTestRootPath(fsView, "/user/dirX/dirY"));
Assert.assertEquals(new Path(targetTestRoot,"user/dirX/dirY"),
fsView.resolvePath(new Path("/user/dirX/dirY")));
}
- @Test(expected=FileNotFoundException.class)
+ @Test(expected=FileNotFoundException.class)
public void testResolvePathDanglingLink() throws IOException {
fsView.resolvePath(new Path("/danglingLink"));
}
-
- @Test(expected=FileNotFoundException.class)
+
+ @Test(expected=FileNotFoundException.class)
public void testResolvePathMissingThroughMountPoints() throws IOException {
fsView.resolvePath(new Path("/user/nonExisting"));
}
-
- @Test(expected=FileNotFoundException.class)
+
+ @Test(expected=FileNotFoundException.class)
public void testResolvePathMissingThroughMountPoints2() throws IOException {
fsView.mkdirs(
fileSystemTestHelper.getTestRootPath(fsView, "/user/dirX"));
fsView.resolvePath(new Path("/user/dirX/nonExisting"));
}
-
+
/**
- * Test modify operations (create, mkdir, rename, etc)
+ * Test modify operations (create, mkdir, rename, etc)
* on internal dirs of mount table
* These operations should fail since the mount table is read-only or
* because the internal dir that it is trying to create already
* exits.
*/
-
-
+
+
// Mkdir on existing internal mount table succeed except for /
- @Test(expected=AccessControlException.class)
+ @Test(expected=AccessControlException.class)
public void testInternalMkdirSlash() throws IOException {
fsView.mkdirs(fileSystemTestHelper.getTestRootPath(fsView, "/"));
}
-
+
public void testInternalMkdirExisting1() throws IOException {
- Assert.assertTrue("mkdir of existing dir should succeed",
+ Assert.assertTrue("mkdir of existing dir should succeed",
fsView.mkdirs(fileSystemTestHelper.getTestRootPath(fsView,
"/internalDir")));
}
public void testInternalMkdirExisting2() throws IOException {
- Assert.assertTrue("mkdir of existing dir should succeed",
+ Assert.assertTrue("mkdir of existing dir should succeed",
fsView.mkdirs(fileSystemTestHelper.getTestRootPath(fsView,
"/internalDir/linkToDir2")));
}
-
+
// Mkdir for new internal mount table should fail
- @Test(expected=AccessControlException.class)
+ @Test(expected=AccessControlException.class)
public void testInternalMkdirNew() throws IOException {
fsView.mkdirs(fileSystemTestHelper.getTestRootPath(fsView, "/dirNew"));
}
- @Test(expected=AccessControlException.class)
+ @Test(expected=AccessControlException.class)
public void testInternalMkdirNew2() throws IOException {
fsView.mkdirs(fileSystemTestHelper.getTestRootPath(fsView,
"/internalDir/dirNew"));
}
-
+
// Create File on internal mount table should fail
-
- @Test(expected=AccessControlException.class)
+
+ @Test(expected=AccessControlException.class)
public void testInternalCreate1() throws IOException {
fileSystemTestHelper.createFile(fsView, "/foo"); // 1 component
}
-
- @Test(expected=AccessControlException.class)
+
+ @Test(expected=AccessControlException.class)
public void testInternalCreate2() throws IOException { // 2 component
fileSystemTestHelper.createFile(fsView, "/internalDir/foo");
}
-
- @Test(expected=AccessControlException.class)
+
+ @Test(expected=AccessControlException.class)
public void testInternalCreateMissingDir() throws IOException {
fileSystemTestHelper.createFile(fsView, "/missingDir/foo");
}
-
- @Test(expected=AccessControlException.class)
+
+ @Test(expected=AccessControlException.class)
public void testInternalCreateMissingDir2() throws IOException {
fileSystemTestHelper.createFile(fsView, "/missingDir/miss2/foo");
}
-
-
- @Test(expected=AccessControlException.class)
+
+
+ @Test(expected=AccessControlException.class)
public void testInternalCreateMissingDir3() throws IOException {
fileSystemTestHelper.createFile(fsView, "/internalDir/miss2/foo");
}
-
+
// Delete on internal mount table should fail
-
- @Test(expected=FileNotFoundException.class)
+
+ @Test(expected=FileNotFoundException.class)
public void testInternalDeleteNonExisting() throws IOException {
fsView.delete(new Path("/NonExisting"), false);
}
- @Test(expected=FileNotFoundException.class)
+ @Test(expected=FileNotFoundException.class)
public void testInternalDeleteNonExisting2() throws IOException {
fsView.delete(new Path("/internalDir/NonExisting"), false);
}
- @Test(expected=AccessControlException.class)
+ @Test(expected=AccessControlException.class)
public void testInternalDeleteExisting() throws IOException {
fsView.delete(new Path("/internalDir"), false);
}
- @Test(expected=AccessControlException.class)
+ @Test(expected=AccessControlException.class)
public void testInternalDeleteExisting2() throws IOException {
fsView.getFileStatus(
new Path("/internalDir/linkToDir2")).isDirectory();
fsView.delete(new Path("/internalDir/linkToDir2"), false);
- }
-
+ }
+
@Test
public void testMkdirOfMountLink() throws IOException {
// data exists - mkdirs returns true even though no permission in internal
// mount table
- Assert.assertTrue("mkdir of existing mount link should succeed",
+ Assert.assertTrue("mkdir of existing mount link should succeed",
fsView.mkdirs(new Path("/data")));
}
-
-
+
+
// Rename on internal mount table should fail
-
- @Test(expected=AccessControlException.class)
+
+ @Test(expected=AccessControlException.class)
public void testInternalRename1() throws IOException {
fsView.rename(new Path("/internalDir"), new Path("/newDir"));
}
- @Test(expected=AccessControlException.class)
+ @Test(expected=AccessControlException.class)
public void testInternalRename2() throws IOException {
fsView.getFileStatus(new Path("/internalDir/linkToDir2")).isDirectory();
fsView.rename(new Path("/internalDir/linkToDir2"),
new Path("/internalDir/dir1"));
}
- @Test(expected=AccessControlException.class)
+ @Test(expected=AccessControlException.class)
public void testInternalRename3() throws IOException {
fsView.rename(new Path("/user"), new Path("/internalDir/linkToDir2"));
}
- @Test(expected=AccessControlException.class)
+ @Test(expected=AccessControlException.class)
public void testInternalRenameToSlash() throws IOException {
fsView.rename(new Path("/internalDir/linkToDir2/foo"), new Path("/"));
}
- @Test(expected=AccessControlException.class)
+ @Test(expected=AccessControlException.class)
public void testInternalRenameFromSlash() throws IOException {
fsView.rename(new Path("/"), new Path("/bar"));
}
-
- @Test(expected=AccessControlException.class)
+
+ @Test(expected=AccessControlException.class)
public void testInternalSetOwner() throws IOException {
fsView.setOwner(new Path("/internalDir"), "foo", "bar");
}
-
+
@Test
public void testCreateNonRecursive() throws IOException {
Path path = fileSystemTestHelper.getTestRootPath(fsView, "/user/foo");
fsView.createNonRecursive(path, false, 1024, (short)1, 1024L, null);
FileStatus status = fsView.getFileStatus(new Path("/user/foo"));
Assert.assertTrue("Created file should be type file",
- fsView.isFile(new Path("/user/foo")));
+ fsView.getFileStatus(new Path("/user/foo")).isFile());
Assert.assertTrue("Target of created file should be type file",
- fsTarget.isFile(new Path(targetTestRoot, "user/foo")));
+ fsTarget.getFileStatus(new Path(targetTestRoot, "user/foo")).isFile());
Review Comment:
removed
##########
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/viewfs/InodeTree.java:
##########
@@ -212,6 +225,33 @@ void addLink(final String pathComponent, final
INodeLink<T> link)
}
children.put(pathComponent, link);
}
+
+ void addDirLink(final String pathComponent, final INodeDirLink<T> dirLink)
{
+ children.put(pathComponent, dirLink);
+ }
+ }
+
+ /**
+ * Internal class to represent nested mount points in INodeTree. Nested
mount points are non-leaf nodes in INode tree.
+ * Hence it inherits INodeDir. But it has additional attributes so wrap it
with link.
+ * @param <T>
+ */
+ static class INodeDirLink<T> extends INodeDir<T> {
Review Comment:
isLink() is mostly used inside INodeTree createLink() and resolve() methods
to detect whether the node is leaf(INodeLink) or not.
The current semantics without nested mp are all leaf nodes are
INodeLink(isLink=true), all non-leaf nodes are INodeDir(isLink=false)
With nested mp, INodeDirLink is a non-leaf node but also has link to it so
whether to set isLink() to true is open to discussion. That is why i use
another property isDirLink and use it anywhere.
##########
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/viewfs/InodeTree.java:
##########
@@ -403,24 +455,24 @@ private void createLink(final String src, final String
target,
final String fullPath = curInode.fullPath + (curInode == root ? "" : "/")
+ iPath;
switch (linkType) {
- case SINGLE:
- newLink = new INodeLink<T>(fullPath, aUgi,
- initAndGetTargetFs(), target);
- break;
- case SINGLE_FALLBACK:
- case MERGE_SLASH:
- // Link fallback and link merge slash configuration
- // are handled specially at InodeTree.
- throw new IllegalArgumentException("Unexpected linkType: " + linkType);
- case MERGE:
- case NFLY:
- final String[] targetUris = StringUtils.getStrings(target);
- newLink = new INodeLink<T>(fullPath, aUgi,
- getTargetFileSystem(settings, StringUtils.stringToURI(targetUris)),
- targetUris);
- break;
- default:
- throw new IllegalArgumentException(linkType + ": Infeasible linkType");
+ case SINGLE:
Review Comment:
Removed this change
##########
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/viewfs/InodeTree.java:
##########
@@ -682,6 +736,25 @@ protected InodeTree(final Configuration config, final
String viewName,
}
}
+ private Collection<LinkEntry> getLinkEntries(List<LinkEntry> linkEntries) {
+ if (isNestedMountPointSupported) {
Review Comment:
Added
##########
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/viewfs/InodeTree.java:
##########
@@ -377,9 +422,16 @@ private void createLink(final String src, final String
target,
nextInode = newDir;
}
if (nextInode.isLink()) {
- // Error - expected a dir but got a link
- throw new FileAlreadyExistsException("Path " + nextInode.fullPath +
- " already exists as link");
+ if (isNestedMountPointSupported) {
+ // nested mount detected, add a new INodeDirLink that wraps existing
INodeLink to INodeTree and override existing INodelink
+ INodeDirLink<T> dirLink = new INodeDirLink<T>(nextInode.fullPath,
aUgi, (INodeLink<T>) nextInode);
Review Comment:
I added more details in the jira. The dirLink will only take path and link.
Dirlink is INodeDir traversal wise(there are couple of places in the code that
assumes the non-leaf node is INodeDir so we cannot really DirLink a link here)
and internalDirFs wouldn't apply to DirLink since it always has a nested mp
associated with it.
##########
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/viewfs/InodeTree.java:
##########
@@ -212,6 +227,43 @@ void addLink(final String pathComponent, final
INodeLink<T> link)
}
children.put(pathComponent, link);
}
+
+ void addDirLink(final String pathComponent, final INodeDirLink<T> dirLink)
{
+ children.put(pathComponent, dirLink);
+ }
+ }
+
+ /**
+ * Internal class to represent a INodeDir which also contains a INodeLink.
This is used to support nested mount point
Review Comment:
Fixed
##########
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/viewfs/InodeTree.java:
##########
@@ -212,6 +225,33 @@ void addLink(final String pathComponent, final
INodeLink<T> link)
}
children.put(pathComponent, link);
}
+
+ void addDirLink(final String pathComponent, final INodeDirLink<T> dirLink)
{
+ children.put(pathComponent, dirLink);
+ }
+ }
+
+ /**
+ * Internal class to represent nested mount points in INodeTree. Nested
mount points are non-leaf nodes in INode tree.
+ * Hence it inherits INodeDir. But it has additional attributes so wrap it
with link.
+ * @param <T>
+ */
+ static class INodeDirLink<T> extends INodeDir<T> {
Review Comment:
Added
##########
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/viewfs/InodeTree.java:
##########
@@ -682,6 +748,32 @@ protected InodeTree(final Configuration config, final
String viewName,
}
}
+ /**
+ * Get collection of linkEntry. If nested mount point is supported, sort
mount point src path based on alphabetical order.
+ * The purpose is to group nested path(shortest path always comes first)
during INodeTree creation.
Review Comment:
Fixed
##########
hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/viewfs/ViewFileSystemBaseTest.java:
##########
@@ -167,8 +167,21 @@ void setupMountPoints() {
new Path(targetTestRoot, "missingTarget").toUri());
ConfigUtil.addLink(conf, "/linkToAFile",
new Path(targetTestRoot, "aFile").toUri());
+
+ // Enable nested mount point, ViewFilesystem should support both
non-nested and nested mount points
+ ConfigUtil.setIsNestedMountPointSupported(conf, true);
+ ConfigUtil.addLink(conf, "/user/userA",
+ new Path(targetTestRoot, "user").toUri());
+ ConfigUtil.addLink(conf, "/user/userB",
+ new Path(targetTestRoot, "userB").toUri());
+ ConfigUtil.addLink(conf, "/data/dataA",
+ new Path(targetTestRoot, "dataA").toUri());
+ ConfigUtil.addLink(conf, "/data/dataB",
+ new Path(targetTestRoot, "user").toUri());
+ ConfigUtil.addLink(conf, "/internalDir/linkToDir2/linkToDir2",
+ new Path(targetTestRoot,"linkToDir2").toUri());
Review Comment:
I created a new test class that extends ViewFileSystemBaseTest
##########
hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/viewfs/ViewFileSystemBaseTest.java:
##########
@@ -263,50 +276,79 @@ private void
testOperationsThroughMountLinksInternal(boolean located)
// Create file
fileSystemTestHelper.createFile(fsView, "/user/foo");
Assert.assertTrue("Created file should be type file",
- fsView.isFile(new Path("/user/foo")));
+ fsView.getFileStatus(new Path("/user/foo")).isFile());
Assert.assertTrue("Target of created file should be type file",
- fsTarget.isFile(new Path(targetTestRoot,"user/foo")));
-
+ fsTarget.getFileStatus(new Path(targetTestRoot,"user/foo")).isFile());
+
+ // Create file with nested mp
+ fileSystemTestHelper.createFile(fsView, "/user/userB/foo");
+ Assert.assertTrue("Created file should be type file",
+ fsView.getFileStatus(new Path("/user/userB/foo")).isFile());
+ Assert.assertTrue("Target of created file should be type file",
+ fsTarget.getFileStatus(new Path(targetTestRoot,"userB/foo")).isFile());
+
// Delete the created file
Assert.assertTrue("Delete should succeed",
fsView.delete(new Path("/user/foo"), false));
Assert.assertFalse("File should not exist after delete",
fsView.exists(new Path("/user/foo")));
Assert.assertFalse("Target File should not exist after delete",
fsTarget.exists(new Path(targetTestRoot,"user/foo")));
-
+
+ // Delete the create file with nested mp
+ Assert.assertTrue("Delete should succeed",
+ fsView.delete(new Path("/user/userB/foo"), false));
+ Assert.assertFalse("File should not exist after delete",
+ fsView.exists(new Path("/user/userB/foo")));
+ Assert.assertFalse("Target File should not exist after delete",
+ fsTarget.exists(new Path(targetTestRoot,"userB/foo")));
+
// Create file with a 2 component dirs
fileSystemTestHelper.createFile(fsView, "/internalDir/linkToDir2/foo");
Assert.assertTrue("Created file should be type file",
- fsView.isFile(new Path("/internalDir/linkToDir2/foo")));
+ fsView.getFileStatus(new
Path("/internalDir/linkToDir2/foo")).isFile());
Assert.assertTrue("Target of created file should be type file",
- fsTarget.isFile(new Path(targetTestRoot,"dir2/foo")));
-
+ fsTarget.getFileStatus(new Path(targetTestRoot,"dir2/foo")).isFile());
+
+ // Create file with a 2 component dirs with nested mp
+ fileSystemTestHelper.createFile(fsView,
"/internalDir/linkToDir2/linkToDir2/foo");
+ Assert.assertTrue("Created file should be type file",
+ fsView.getFileStatus(new
Path("/internalDir/linkToDir2/linkToDir2/foo")).isFile());
+ Assert.assertTrue("Target of created file should be type file",
+ fsTarget.getFileStatus(new
Path(targetTestRoot,"linkToDir2/foo")).isFile());
+
// Delete the created file
Assert.assertTrue("Delete should succeed",
fsView.delete(new Path("/internalDir/linkToDir2/foo"), false));
Assert.assertFalse("File should not exist after delete",
fsView.exists(new Path("/internalDir/linkToDir2/foo")));
Assert.assertFalse("Target File should not exist after delete",
fsTarget.exists(new Path(targetTestRoot,"dir2/foo")));
-
-
+
+ // Delete the created file with nested mp
+ Assert.assertTrue("Delete should succeed",
+ fsView.delete(new Path("/internalDir/linkToDir2/linkToDir2/foo"),
false));
+ Assert.assertFalse("File should not exist after delete",
+ fsView.exists(new Path("/internalDir/linkToDir2/linkToDir2/foo")));
+ Assert.assertFalse("Target File should not exist after delete",
+ fsTarget.exists(new Path(targetTestRoot,"linkToDir2/foo")));
+
// Create file with a 3 component dirs
fileSystemTestHelper.createFile(fsView,
"/internalDir/internalDir2/linkToDir3/foo");
Assert.assertTrue("Created file should be type file",
- fsView.isFile(new Path("/internalDir/internalDir2/linkToDir3/foo")));
+ fsView.getFileStatus(new
Path("/internalDir/internalDir2/linkToDir3/foo")).isFile());
Review Comment:
removed deprecated call changes
##########
hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/viewfs/InodeTree.java:
##########
@@ -839,44 +938,46 @@ public ResolveResult<T> resolve(final String p, final
boolean resolveLastCompone
if (nextInode.isLink()) {
final INodeLink<T> link = (INodeLink<T>) nextInode;
- final Path remainingPath;
- if (i >= path.length - 1) {
- remainingPath = SlashPath;
- } else {
- StringBuilder remainingPathStr =
- new StringBuilder("/" + path[i + 1]);
- for (int j = i + 2; j < path.length; ++j) {
- remainingPathStr.append('/').append(path[j]);
- }
- remainingPath = new Path(remainingPathStr.toString());
- }
+ final Path remainingPath = getRemainingPath(path, i + 1);
resolveResult = new ResolveResult<T>(ResultKind.EXTERNAL_DIR,
link.getTargetFileSystem(), nextInode.fullPath, remainingPath,
true);
return resolveResult;
} else if (nextInode.isInternalDir()) {
curInode = (INodeDir<T>) nextInode;
+ // track last resolved nest mount point.
+ if (isNestedMountPointSupported && nextInode.isDirAndLink()) {
+ lastResolvedDirLink = (INodeDirLink<T>) nextInode;
+ lastResolvedDirLinkIndex = i;
+ }
}
}
- // We have resolved to an internal dir in mount table.
Path remainingPath;
- if (resolveLastComponent) {
+ if (isNestedMountPointSupported && lastResolvedDirLink != null) {
+ remainingPath = getRemainingPath(path, lastResolvedDirLinkIndex + 1);
+ resolveResult = new ResolveResult<T>(ResultKind.EXTERNAL_DIR,
lastResolvedDirLink.getLink().getTargetFileSystem(),
+ lastResolvedDirLink.fullPath, remainingPath,true);
+ } else {
+ remainingPath = resolveLastComponent ? SlashPath :
getRemainingPath(path, i);
+ resolveResult = new ResolveResult<T>(ResultKind.INTERNAL_DIR,
curInode.getInternalDirFs(),
+ curInode.fullPath, remainingPath, false);
+ }
+ return resolveResult;
+ }
+
+ private Path getRemainingPath(String[] path, int startIndex) {
Review Comment:
Added
##########
hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/fs/viewfs/ViewFileSystemBaseTest.java:
##########
@@ -662,177 +790,177 @@ public void testResolvePathMountPoints() throws
IOException {
fsView.resolvePath(new Path("/internalDir/internalDir2/linkToDir3")));
}
-
+
@Test
public void testResolvePathThroughMountPoints() throws IOException {
fileSystemTestHelper.createFile(fsView, "/user/foo");
Assert.assertEquals(new Path(targetTestRoot,"user/foo"),
fsView.resolvePath(new Path("/user/foo")));
-
+
fsView.mkdirs(
fileSystemTestHelper.getTestRootPath(fsView, "/user/dirX"));
Assert.assertEquals(new Path(targetTestRoot,"user/dirX"),
fsView.resolvePath(new Path("/user/dirX")));
-
+
fsView.mkdirs(
fileSystemTestHelper.getTestRootPath(fsView, "/user/dirX/dirY"));
Assert.assertEquals(new Path(targetTestRoot,"user/dirX/dirY"),
fsView.resolvePath(new Path("/user/dirX/dirY")));
}
- @Test(expected=FileNotFoundException.class)
+ @Test(expected=FileNotFoundException.class)
public void testResolvePathDanglingLink() throws IOException {
fsView.resolvePath(new Path("/danglingLink"));
}
-
- @Test(expected=FileNotFoundException.class)
+
+ @Test(expected=FileNotFoundException.class)
public void testResolvePathMissingThroughMountPoints() throws IOException {
fsView.resolvePath(new Path("/user/nonExisting"));
}
-
- @Test(expected=FileNotFoundException.class)
+
+ @Test(expected=FileNotFoundException.class)
public void testResolvePathMissingThroughMountPoints2() throws IOException {
fsView.mkdirs(
fileSystemTestHelper.getTestRootPath(fsView, "/user/dirX"));
fsView.resolvePath(new Path("/user/dirX/nonExisting"));
}
-
+
/**
- * Test modify operations (create, mkdir, rename, etc)
+ * Test modify operations (create, mkdir, rename, etc)
* on internal dirs of mount table
* These operations should fail since the mount table is read-only or
* because the internal dir that it is trying to create already
* exits.
*/
-
-
+
+
// Mkdir on existing internal mount table succeed except for /
- @Test(expected=AccessControlException.class)
+ @Test(expected=AccessControlException.class)
public void testInternalMkdirSlash() throws IOException {
fsView.mkdirs(fileSystemTestHelper.getTestRootPath(fsView, "/"));
}
-
+
public void testInternalMkdirExisting1() throws IOException {
- Assert.assertTrue("mkdir of existing dir should succeed",
+ Assert.assertTrue("mkdir of existing dir should succeed",
fsView.mkdirs(fileSystemTestHelper.getTestRootPath(fsView,
"/internalDir")));
}
public void testInternalMkdirExisting2() throws IOException {
- Assert.assertTrue("mkdir of existing dir should succeed",
+ Assert.assertTrue("mkdir of existing dir should succeed",
fsView.mkdirs(fileSystemTestHelper.getTestRootPath(fsView,
"/internalDir/linkToDir2")));
}
-
+
// Mkdir for new internal mount table should fail
- @Test(expected=AccessControlException.class)
+ @Test(expected=AccessControlException.class)
public void testInternalMkdirNew() throws IOException {
fsView.mkdirs(fileSystemTestHelper.getTestRootPath(fsView, "/dirNew"));
}
- @Test(expected=AccessControlException.class)
+ @Test(expected=AccessControlException.class)
public void testInternalMkdirNew2() throws IOException {
fsView.mkdirs(fileSystemTestHelper.getTestRootPath(fsView,
"/internalDir/dirNew"));
}
-
+
// Create File on internal mount table should fail
-
- @Test(expected=AccessControlException.class)
+
+ @Test(expected=AccessControlException.class)
public void testInternalCreate1() throws IOException {
fileSystemTestHelper.createFile(fsView, "/foo"); // 1 component
}
-
- @Test(expected=AccessControlException.class)
+
+ @Test(expected=AccessControlException.class)
public void testInternalCreate2() throws IOException { // 2 component
fileSystemTestHelper.createFile(fsView, "/internalDir/foo");
}
-
- @Test(expected=AccessControlException.class)
+
+ @Test(expected=AccessControlException.class)
public void testInternalCreateMissingDir() throws IOException {
fileSystemTestHelper.createFile(fsView, "/missingDir/foo");
}
-
- @Test(expected=AccessControlException.class)
+
+ @Test(expected=AccessControlException.class)
public void testInternalCreateMissingDir2() throws IOException {
fileSystemTestHelper.createFile(fsView, "/missingDir/miss2/foo");
}
-
-
- @Test(expected=AccessControlException.class)
+
+
+ @Test(expected=AccessControlException.class)
public void testInternalCreateMissingDir3() throws IOException {
fileSystemTestHelper.createFile(fsView, "/internalDir/miss2/foo");
}
-
+
// Delete on internal mount table should fail
-
- @Test(expected=FileNotFoundException.class)
+
+ @Test(expected=FileNotFoundException.class)
public void testInternalDeleteNonExisting() throws IOException {
fsView.delete(new Path("/NonExisting"), false);
}
- @Test(expected=FileNotFoundException.class)
+ @Test(expected=FileNotFoundException.class)
public void testInternalDeleteNonExisting2() throws IOException {
fsView.delete(new Path("/internalDir/NonExisting"), false);
}
- @Test(expected=AccessControlException.class)
+ @Test(expected=AccessControlException.class)
public void testInternalDeleteExisting() throws IOException {
fsView.delete(new Path("/internalDir"), false);
}
- @Test(expected=AccessControlException.class)
+ @Test(expected=AccessControlException.class)
public void testInternalDeleteExisting2() throws IOException {
fsView.getFileStatus(
new Path("/internalDir/linkToDir2")).isDirectory();
fsView.delete(new Path("/internalDir/linkToDir2"), false);
- }
-
+ }
+
@Test
public void testMkdirOfMountLink() throws IOException {
// data exists - mkdirs returns true even though no permission in internal
// mount table
- Assert.assertTrue("mkdir of existing mount link should succeed",
+ Assert.assertTrue("mkdir of existing mount link should succeed",
fsView.mkdirs(new Path("/data")));
}
-
-
+
+
// Rename on internal mount table should fail
-
- @Test(expected=AccessControlException.class)
+
+ @Test(expected=AccessControlException.class)
public void testInternalRename1() throws IOException {
fsView.rename(new Path("/internalDir"), new Path("/newDir"));
}
- @Test(expected=AccessControlException.class)
+ @Test(expected=AccessControlException.class)
public void testInternalRename2() throws IOException {
fsView.getFileStatus(new Path("/internalDir/linkToDir2")).isDirectory();
fsView.rename(new Path("/internalDir/linkToDir2"),
new Path("/internalDir/dir1"));
}
- @Test(expected=AccessControlException.class)
+ @Test(expected=AccessControlException.class)
public void testInternalRename3() throws IOException {
fsView.rename(new Path("/user"), new Path("/internalDir/linkToDir2"));
}
- @Test(expected=AccessControlException.class)
+ @Test(expected=AccessControlException.class)
public void testInternalRenameToSlash() throws IOException {
fsView.rename(new Path("/internalDir/linkToDir2/foo"), new Path("/"));
}
- @Test(expected=AccessControlException.class)
+ @Test(expected=AccessControlException.class)
public void testInternalRenameFromSlash() throws IOException {
fsView.rename(new Path("/"), new Path("/bar"));
}
-
- @Test(expected=AccessControlException.class)
+
+ @Test(expected=AccessControlException.class)
public void testInternalSetOwner() throws IOException {
fsView.setOwner(new Path("/internalDir"), "foo", "bar");
}
-
+
@Test
public void testCreateNonRecursive() throws IOException {
Path path = fileSystemTestHelper.getTestRootPath(fsView, "/user/foo");
fsView.createNonRecursive(path, false, 1024, (short)1, 1024L, null);
FileStatus status = fsView.getFileStatus(new Path("/user/foo"));
Assert.assertTrue("Created file should be type file",
- fsView.isFile(new Path("/user/foo")));
+ fsView.getFileStatus(new Path("/user/foo")).isFile());
Review Comment:
removed
Issue Time Tracking
-------------------
Worklog Id: (was: 768627)
Time Spent: 3h 40m (was: 3.5h)
> Support nested mount points in INodeTree
> ----------------------------------------
>
> Key: HADOOP-18193
> URL: https://issues.apache.org/jira/browse/HADOOP-18193
> Project: Hadoop Common
> Issue Type: Improvement
> Components: viewfs
> Affects Versions: 2.10.0
> Reporter: Lei Yang
> Assignee: Lei Yang
> Priority: Major
> Labels: pull-request-available
> Attachments: Nested Mount Point in ViewFs.pdf
>
> Time Spent: 3h 40m
> Remaining Estimate: 0h
>
> Defining following client mount table config is not supported in INodeTree
> and will throw FileAlreadyExistsException
>
> {code:java}
> fs.viewfs.mounttable.link./foo/bar=hdfs://nn1/foo/bar
> fs.viewfs.mounttable.link./foo=hdfs://nn02/foo
> {code}
> INodeTree has 2 methods that need change to support nested mount points.
> {code:java}
> createLink(): build INodeTree during fs init.
> resolve(): resolve path in INodeTree with viewfs apis.
> {code}
> ViewFileSystem and ViewFs maintains an INodeTree instance(fsState) in both
> classes and call fsState.resolve(..) to resolve path to specific mount point.
> INodeTree.resolve encapsulates the logic of nested mount point resolving. So
> no changes are expected in both classes.
> AC:
> # INodeTree.createlink should support creating nested mount
> points.(INodeTree is constructed during fs init)
> # INodeTree.resolve should support resolve path based on nested mount
> points. (INodeTree.resolve is used in viewfs apis)
> # No regression in existing ViewFileSystem and ViewFs apis.
> # Ensure some important apis are not broken with nested mount points.
> (Rename, getContentSummary, listStatus...)
>
> Spec:
> Please review attached pdf for spec about this feature.
--
This message was sent by Atlassian Jira
(v8.20.7#820007)
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]