HDFS-14369. RBF: Fix trailing / for webhdfs. Contributed by Akira Ajisaka.
This commit is contained in:
parent
dd8c2b92df
commit
0f9b8d7a75
@ -57,6 +57,7 @@ import org.apache.hadoop.hdfs.server.federation.store.StateStoreUnavailableExcep
|
|||||||
import org.apache.hadoop.hdfs.server.federation.store.protocol.GetMountTableEntriesRequest;
|
import org.apache.hadoop.hdfs.server.federation.store.protocol.GetMountTableEntriesRequest;
|
||||||
import org.apache.hadoop.hdfs.server.federation.store.protocol.GetMountTableEntriesResponse;
|
import org.apache.hadoop.hdfs.server.federation.store.protocol.GetMountTableEntriesResponse;
|
||||||
import org.apache.hadoop.hdfs.server.federation.store.records.MountTable;
|
import org.apache.hadoop.hdfs.server.federation.store.records.MountTable;
|
||||||
|
import org.apache.hadoop.hdfs.tools.federation.RouterAdmin;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -399,12 +400,13 @@ public class MountTableResolver
|
|||||||
/**
|
/**
|
||||||
* Build the path location to insert into the cache atomically. It must hold
|
* Build the path location to insert into the cache atomically. It must hold
|
||||||
* the read lock.
|
* the read lock.
|
||||||
* @param path Path to check/insert.
|
* @param str Path to check/insert.
|
||||||
* @return New remote location.
|
* @return New remote location.
|
||||||
* @throws IOException If it cannot find the location.
|
* @throws IOException If it cannot find the location.
|
||||||
*/
|
*/
|
||||||
public PathLocation lookupLocation(final String path) throws IOException {
|
public PathLocation lookupLocation(final String str) throws IOException {
|
||||||
PathLocation ret = null;
|
PathLocation ret = null;
|
||||||
|
final String path = RouterAdmin.normalizeFileSystemPath(str);
|
||||||
MountTable entry = findDeepest(path);
|
MountTable entry = findDeepest(path);
|
||||||
if (entry != null) {
|
if (entry != null) {
|
||||||
ret = buildLocation(path, entry);
|
ret = buildLocation(path, entry);
|
||||||
@ -432,12 +434,13 @@ public class MountTableResolver
|
|||||||
*/
|
*/
|
||||||
public MountTable getMountPoint(final String path) throws IOException {
|
public MountTable getMountPoint(final String path) throws IOException {
|
||||||
verifyMountTable();
|
verifyMountTable();
|
||||||
return findDeepest(path);
|
return findDeepest(RouterAdmin.normalizeFileSystemPath(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getMountPoints(final String path) throws IOException {
|
public List<String> getMountPoints(final String str) throws IOException {
|
||||||
verifyMountTable();
|
verifyMountTable();
|
||||||
|
final String path = RouterAdmin.normalizeFileSystemPath(str);
|
||||||
|
|
||||||
Set<String> children = new TreeSet<>();
|
Set<String> children = new TreeSet<>();
|
||||||
readLock.lock();
|
readLock.lock();
|
||||||
@ -493,8 +496,7 @@ public class MountTableResolver
|
|||||||
*/
|
*/
|
||||||
public List<MountTable> getMounts(final String path) throws IOException {
|
public List<MountTable> getMounts(final String path) throws IOException {
|
||||||
verifyMountTable();
|
verifyMountTable();
|
||||||
|
return getTreeValues(RouterAdmin.normalizeFileSystemPath(path), false);
|
||||||
return getTreeValues(path, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -26,12 +26,12 @@ import java.util.LinkedList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.apache.hadoop.classification.InterfaceAudience.Private;
|
import org.apache.hadoop.classification.InterfaceAudience.Private;
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.conf.Configured;
|
import org.apache.hadoop.conf.Configured;
|
||||||
import org.apache.hadoop.fs.CommonConfigurationKeys;
|
import org.apache.hadoop.fs.CommonConfigurationKeys;
|
||||||
import org.apache.hadoop.fs.Path;
|
|
||||||
import org.apache.hadoop.fs.permission.FsPermission;
|
import org.apache.hadoop.fs.permission.FsPermission;
|
||||||
import org.apache.hadoop.hdfs.DFSConfigKeys;
|
import org.apache.hadoop.hdfs.DFSConfigKeys;
|
||||||
import org.apache.hadoop.hdfs.HdfsConfiguration;
|
import org.apache.hadoop.hdfs.HdfsConfiguration;
|
||||||
@ -93,6 +93,9 @@ public class RouterAdmin extends Configured implements Tool {
|
|||||||
|
|
||||||
private RouterClient client;
|
private RouterClient client;
|
||||||
|
|
||||||
|
/** Pre-compiled regular expressions to detect duplicated slashes. */
|
||||||
|
private static final Pattern SLASHES = Pattern.compile("/+");
|
||||||
|
|
||||||
public static void main(String[] argv) throws Exception {
|
public static void main(String[] argv) throws Exception {
|
||||||
Configuration conf = new HdfsConfiguration();
|
Configuration conf = new HdfsConfiguration();
|
||||||
RouterAdmin admin = new RouterAdmin(conf);
|
RouterAdmin admin = new RouterAdmin(conf);
|
||||||
@ -1062,12 +1065,15 @@ public class RouterAdmin extends Configured implements Tool {
|
|||||||
/**
|
/**
|
||||||
* Normalize a path for that filesystem.
|
* Normalize a path for that filesystem.
|
||||||
*
|
*
|
||||||
* @param path Path to normalize.
|
* @param str Path to normalize. The path doesn't have scheme or authority.
|
||||||
* @return Normalized path.
|
* @return Normalized path.
|
||||||
*/
|
*/
|
||||||
private static String normalizeFileSystemPath(final String path) {
|
public static String normalizeFileSystemPath(final String str) {
|
||||||
Path normalizedPath = new Path(path);
|
String path = SLASHES.matcher(str).replaceAll("/");
|
||||||
return normalizedPath.toString();
|
if (path.length() > 1 && path.endsWith("/")) {
|
||||||
|
path = path.substring(0, path.length()-1);
|
||||||
|
}
|
||||||
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -184,6 +184,23 @@ public class TestMountTableResolver {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDestinationOfConsecutiveSlash() throws IOException {
|
||||||
|
// Check files
|
||||||
|
assertEquals("1->/tesfile1.txt",
|
||||||
|
mountTable.getDestinationForPath("//tesfile1.txt///").toString());
|
||||||
|
|
||||||
|
assertEquals("3->/user/testfile2.txt",
|
||||||
|
mountTable.getDestinationForPath("/user///testfile2.txt").toString());
|
||||||
|
|
||||||
|
assertEquals("2->/user/test/testfile3.txt",
|
||||||
|
mountTable.getDestinationForPath("///user/a/testfile3.txt").toString());
|
||||||
|
|
||||||
|
assertEquals("3->/user/b/testfile4.txt",
|
||||||
|
mountTable.getDestinationForPath("/user/b/testfile4.txt//").toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDefaultNameServiceEnable() throws IOException {
|
public void testDefaultNameServiceEnable() throws IOException {
|
||||||
assertTrue(mountTable.isDefaultNSEnable());
|
assertTrue(mountTable.isDefaultNSEnable());
|
||||||
@ -232,62 +249,120 @@ public class TestMountTableResolver {
|
|||||||
// Check get the mount table entry for a path
|
// Check get the mount table entry for a path
|
||||||
MountTable mtEntry;
|
MountTable mtEntry;
|
||||||
mtEntry = mountTable.getMountPoint("/");
|
mtEntry = mountTable.getMountPoint("/");
|
||||||
assertTrue(mtEntry.getSourcePath().equals("/"));
|
assertEquals("/", mtEntry.getSourcePath());
|
||||||
|
|
||||||
mtEntry = mountTable.getMountPoint("/user");
|
mtEntry = mountTable.getMountPoint("/user");
|
||||||
assertTrue(mtEntry.getSourcePath().equals("/user"));
|
assertEquals("/user", mtEntry.getSourcePath());
|
||||||
|
|
||||||
mtEntry = mountTable.getMountPoint("/user/a");
|
mtEntry = mountTable.getMountPoint("/user/a");
|
||||||
assertTrue(mtEntry.getSourcePath().equals("/user/a"));
|
assertEquals("/user/a", mtEntry.getSourcePath());
|
||||||
|
|
||||||
mtEntry = mountTable.getMountPoint("/user/a/");
|
mtEntry = mountTable.getMountPoint("/user/a/");
|
||||||
assertTrue(mtEntry.getSourcePath().equals("/user/a"));
|
assertEquals("/user/a", mtEntry.getSourcePath());
|
||||||
|
|
||||||
mtEntry = mountTable.getMountPoint("/user/a/11");
|
mtEntry = mountTable.getMountPoint("/user/a/11");
|
||||||
assertTrue(mtEntry.getSourcePath().equals("/user/a"));
|
assertEquals("/user/a", mtEntry.getSourcePath());
|
||||||
|
|
||||||
mtEntry = mountTable.getMountPoint("/user/a1");
|
mtEntry = mountTable.getMountPoint("/user/a1");
|
||||||
assertTrue(mtEntry.getSourcePath().equals("/user"));
|
assertEquals("/user", mtEntry.getSourcePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetMountPointOfConsecutiveSlashes() throws IOException {
|
||||||
|
// Check get the mount table entry for a path
|
||||||
|
MountTable mtEntry;
|
||||||
|
mtEntry = mountTable.getMountPoint("///");
|
||||||
|
assertEquals("/", mtEntry.getSourcePath());
|
||||||
|
|
||||||
|
mtEntry = mountTable.getMountPoint("///user//");
|
||||||
|
assertEquals("/user", mtEntry.getSourcePath());
|
||||||
|
|
||||||
|
mtEntry = mountTable.getMountPoint("/user///a");
|
||||||
|
assertEquals("/user/a", mtEntry.getSourcePath());
|
||||||
|
|
||||||
|
mtEntry = mountTable.getMountPoint("/user/a////");
|
||||||
|
assertEquals("/user/a", mtEntry.getSourcePath());
|
||||||
|
|
||||||
|
mtEntry = mountTable.getMountPoint("///user/a/11//");
|
||||||
|
assertEquals("/user/a", mtEntry.getSourcePath());
|
||||||
|
|
||||||
|
mtEntry = mountTable.getMountPoint("/user///a1///");
|
||||||
|
assertEquals("/user", mtEntry.getSourcePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTrailingSlashInInputPath() throws IOException {
|
||||||
|
// Check mount points beneath the path with trailing slash.
|
||||||
|
getMountPoints(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetMountPoints() throws IOException {
|
public void testGetMountPoints() throws IOException {
|
||||||
|
// Check mount points beneath the path without trailing slash.
|
||||||
|
getMountPoints(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void getMountPoints(boolean trailingSlash) throws IOException {
|
||||||
// Check getting all mount points (virtual and real) beneath a path
|
// Check getting all mount points (virtual and real) beneath a path
|
||||||
List<String> mounts = mountTable.getMountPoints("/");
|
List<String> mounts = mountTable.getMountPoints("/");
|
||||||
assertEquals(5, mounts.size());
|
assertEquals(5, mounts.size());
|
||||||
compareLists(mounts, new String[] {"tmp", "user", "usr",
|
compareLists(mounts, new String[] {"tmp", "user", "usr",
|
||||||
"readonly", "multi"});
|
"readonly", "multi"});
|
||||||
|
|
||||||
mounts = mountTable.getMountPoints("/user");
|
String path = trailingSlash ? "/user/" : "/user";
|
||||||
|
mounts = mountTable.getMountPoints(path);
|
||||||
assertEquals(2, mounts.size());
|
assertEquals(2, mounts.size());
|
||||||
compareLists(mounts, new String[] {"a", "b"});
|
compareLists(mounts, new String[] {"a", "b"});
|
||||||
|
|
||||||
mounts = mountTable.getMountPoints("/user/a");
|
path = trailingSlash ? "/user/a/" : "/user/a";
|
||||||
|
mounts = mountTable.getMountPoints(path);
|
||||||
assertEquals(1, mounts.size());
|
assertEquals(1, mounts.size());
|
||||||
compareLists(mounts, new String[] {"demo"});
|
compareLists(mounts, new String[] {"demo"});
|
||||||
|
|
||||||
mounts = mountTable.getMountPoints("/user/a/demo");
|
path = trailingSlash ? "/user/a/demo/" : "/user/a/demo";
|
||||||
|
mounts = mountTable.getMountPoints(path);
|
||||||
assertEquals(1, mounts.size());
|
assertEquals(1, mounts.size());
|
||||||
compareLists(mounts, new String[] {"test"});
|
compareLists(mounts, new String[] {"test"});
|
||||||
|
|
||||||
mounts = mountTable.getMountPoints("/user/a/demo/test");
|
path = trailingSlash ? "/user/a/demo/test/" : "/user/a/demo/test";
|
||||||
|
mounts = mountTable.getMountPoints(path);
|
||||||
assertEquals(2, mounts.size());
|
assertEquals(2, mounts.size());
|
||||||
compareLists(mounts, new String[] {"a", "b"});
|
compareLists(mounts, new String[] {"a", "b"});
|
||||||
|
|
||||||
mounts = mountTable.getMountPoints("/tmp");
|
path = trailingSlash ? "/tmp/" : "/tmp";
|
||||||
|
mounts = mountTable.getMountPoints(path);
|
||||||
assertEquals(0, mounts.size());
|
assertEquals(0, mounts.size());
|
||||||
|
|
||||||
mounts = mountTable.getMountPoints("/t");
|
path = trailingSlash ? "/t/" : "/t";
|
||||||
|
mounts = mountTable.getMountPoints(path);
|
||||||
assertNull(mounts);
|
assertNull(mounts);
|
||||||
|
|
||||||
mounts = mountTable.getMountPoints("/unknownpath");
|
path = trailingSlash ? "/unknownpath/" : "/unknownpath";
|
||||||
|
mounts = mountTable.getMountPoints(path);
|
||||||
assertNull(mounts);
|
assertNull(mounts);
|
||||||
|
|
||||||
mounts = mountTable.getMountPoints("/multi");
|
path = trailingSlash ? "/multi/" : "/multi";
|
||||||
|
mounts = mountTable.getMountPoints(path);
|
||||||
assertEquals(0, mounts.size());
|
assertEquals(0, mounts.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSuccessiveSlashesInInputPath() throws IOException {
|
||||||
|
// Check getting all mount points (virtual and real) beneath a path
|
||||||
|
List<String> mounts = mountTable.getMountPoints("///");
|
||||||
|
assertEquals(5, mounts.size());
|
||||||
|
compareLists(mounts, new String[] {"tmp", "user", "usr",
|
||||||
|
"readonly", "multi"});
|
||||||
|
String path = "///user//";
|
||||||
|
mounts = mountTable.getMountPoints(path);
|
||||||
|
assertEquals(2, mounts.size());
|
||||||
|
compareLists(mounts, new String[] {"a", "b"});
|
||||||
|
path = "/user///a";
|
||||||
|
mounts = mountTable.getMountPoints(path);
|
||||||
|
assertEquals(1, mounts.size());
|
||||||
|
compareLists(mounts, new String[] {"demo"});
|
||||||
|
}
|
||||||
|
|
||||||
private void compareRecords(List<MountTable> list1, String[] list2) {
|
private void compareRecords(List<MountTable> list1, String[] list2) {
|
||||||
assertEquals(list1.size(), list2.length);
|
assertEquals(list1.size(), list2.length);
|
||||||
for (String item : list2) {
|
for (String item : list2) {
|
||||||
@ -334,6 +409,26 @@ public class TestMountTableResolver {
|
|||||||
compareRecords(records, new String[] {"/multi"});
|
compareRecords(records, new String[] {"/multi"});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetMountsOfConsecutiveSlashes() throws IOException {
|
||||||
|
// Check listing the mount table records at or beneath a path
|
||||||
|
List<MountTable> records = mountTable.getMounts("///");
|
||||||
|
assertEquals(10, records.size());
|
||||||
|
compareRecords(records, new String[] {"/", "/tmp", "/user", "/usr/bin",
|
||||||
|
"user/a", "/user/a/demo/a", "/user/a/demo/b", "/user/b/file1.txt",
|
||||||
|
"readonly", "multi"});
|
||||||
|
|
||||||
|
records = mountTable.getMounts("/user///");
|
||||||
|
assertEquals(5, records.size());
|
||||||
|
compareRecords(records, new String[] {"/user", "/user/a/demo/a",
|
||||||
|
"/user/a/demo/b", "user/a", "/user/b/file1.txt"});
|
||||||
|
|
||||||
|
records = mountTable.getMounts("///user///a");
|
||||||
|
assertEquals(3, records.size());
|
||||||
|
compareRecords(records,
|
||||||
|
new String[] {"/user/a/demo/a", "/user/a/demo/b", "/user/a"});
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRemoveSubTree()
|
public void testRemoveSubTree()
|
||||||
throws UnsupportedOperationException, IOException {
|
throws UnsupportedOperationException, IOException {
|
||||||
|
@ -442,4 +442,36 @@ public class TestRouterMountTable {
|
|||||||
"Directory/File does not exist /mount/file",
|
"Directory/File does not exist /mount/file",
|
||||||
() -> routerFs.setOwner(new Path("/mount/file"), "user", "group"));
|
() -> routerFs.setOwner(new Path("/mount/file"), "user", "group"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regression test for HDFS-14369.
|
||||||
|
* Verify that getListing works with the path with trailing slash.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testGetListingWithTrailingSlash() throws IOException {
|
||||||
|
try {
|
||||||
|
// Add mount table entry
|
||||||
|
MountTable addEntry = MountTable.newInstance("/testlist",
|
||||||
|
Collections.singletonMap("ns0", "/testlist"));
|
||||||
|
assertTrue(addMountTable(addEntry));
|
||||||
|
addEntry = MountTable.newInstance("/testlist/tmp0",
|
||||||
|
Collections.singletonMap("ns0", "/testlist/tmp0"));
|
||||||
|
assertTrue(addMountTable(addEntry));
|
||||||
|
addEntry = MountTable.newInstance("/testlist/tmp1",
|
||||||
|
Collections.singletonMap("ns1", "/testlist/tmp1"));
|
||||||
|
assertTrue(addMountTable(addEntry));
|
||||||
|
|
||||||
|
nnFs0.mkdirs(new Path("/testlist/tmp0"));
|
||||||
|
nnFs1.mkdirs(new Path("/testlist/tmp1"));
|
||||||
|
// Fetch listing
|
||||||
|
DirectoryListing list = routerProtocol.getListing(
|
||||||
|
"/testlist/", HdfsFileStatus.EMPTY_NAME, false);
|
||||||
|
HdfsFileStatus[] statuses = list.getPartialListing();
|
||||||
|
// should return tmp0 and tmp1
|
||||||
|
assertEquals(2, statuses.length);
|
||||||
|
} finally {
|
||||||
|
nnFs0.delete(new Path("/testlist/tmp0"), true);
|
||||||
|
nnFs1.delete(new Path("/testlist/tmp1"), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user