HDFS-15436. Default mount table name used by ViewFileSystem should be configurable (#2100)

* HDFS-15436. Default mount table name used by ViewFileSystem should be configurable

* Replace Constants.CONFIG_VIEWFS_DEFAULT_MOUNT_TABLE use in tests

* Address Uma's comments on PR#2100

* Sort lists in test to match without concern to order

* Address comments, fix checkstyle and fix failing tests

* Fix checkstyle

(cherry picked from commit bed0a3a374)
This commit is contained in:
Virajith Jalaparti 2020-06-26 13:19:16 -07:00 committed by Uma Maheswara Rao G
parent d030f4b2a6
commit 704b273ec8
9 changed files with 141 additions and 28 deletions

View File

@ -66,8 +66,7 @@ public class ConfigUtil {
*/ */
public static void addLink(final Configuration conf, final String src, public static void addLink(final Configuration conf, final String src,
final URI target) { final URI target) {
addLink( conf, Constants.CONFIG_VIEWFS_DEFAULT_MOUNT_TABLE, addLink(conf, getDefaultMountTableName(conf), src, target);
src, target);
} }
/** /**
@ -88,8 +87,7 @@ public class ConfigUtil {
* @param target * @param target
*/ */
public static void addLinkMergeSlash(Configuration conf, final URI target) { public static void addLinkMergeSlash(Configuration conf, final URI target) {
addLinkMergeSlash(conf, Constants.CONFIG_VIEWFS_DEFAULT_MOUNT_TABLE, addLinkMergeSlash(conf, getDefaultMountTableName(conf), target);
target);
} }
/** /**
@ -110,8 +108,7 @@ public class ConfigUtil {
* @param target * @param target
*/ */
public static void addLinkFallback(Configuration conf, final URI target) { public static void addLinkFallback(Configuration conf, final URI target) {
addLinkFallback(conf, Constants.CONFIG_VIEWFS_DEFAULT_MOUNT_TABLE, addLinkFallback(conf, getDefaultMountTableName(conf), target);
target);
} }
/** /**
@ -132,7 +129,7 @@ public class ConfigUtil {
* @param targets * @param targets
*/ */
public static void addLinkMerge(Configuration conf, final URI[] targets) { public static void addLinkMerge(Configuration conf, final URI[] targets) {
addLinkMerge(conf, Constants.CONFIG_VIEWFS_DEFAULT_MOUNT_TABLE, targets); addLinkMerge(conf, getDefaultMountTableName(conf), targets);
} }
/** /**
@ -166,8 +163,7 @@ public class ConfigUtil {
public static void addLinkNfly(final Configuration conf, final String src, public static void addLinkNfly(final Configuration conf, final String src,
final URI ... targets) { final URI ... targets) {
addLinkNfly(conf, Constants.CONFIG_VIEWFS_DEFAULT_MOUNT_TABLE, src, null, addLinkNfly(conf, getDefaultMountTableName(conf), src, null, targets);
targets);
} }
/** /**
@ -177,8 +173,7 @@ public class ConfigUtil {
*/ */
public static void setHomeDirConf(final Configuration conf, public static void setHomeDirConf(final Configuration conf,
final String homedir) { final String homedir) {
setHomeDirConf( conf, setHomeDirConf(conf, getDefaultMountTableName(conf), homedir);
Constants.CONFIG_VIEWFS_DEFAULT_MOUNT_TABLE, homedir);
} }
/** /**
@ -202,7 +197,7 @@ public class ConfigUtil {
* @return home dir value, null if variable is not in conf * @return home dir value, null if variable is not in conf
*/ */
public static String getHomeDirValue(final Configuration conf) { public static String getHomeDirValue(final Configuration conf) {
return getHomeDirValue(conf, Constants.CONFIG_VIEWFS_DEFAULT_MOUNT_TABLE); return getHomeDirValue(conf, getDefaultMountTableName(conf));
} }
/** /**
@ -216,4 +211,18 @@ public class ConfigUtil {
return conf.get(getConfigViewFsPrefix(mountTableName) + "." + return conf.get(getConfigViewFsPrefix(mountTableName) + "." +
Constants.CONFIG_VIEWFS_HOMEDIR); Constants.CONFIG_VIEWFS_HOMEDIR);
} }
/**
* Get the name of the default mount table to use. If
* {@link Constants#CONFIG_VIEWFS_DEFAULT_MOUNT_TABLE_NAME_KEY} is specified,
* it's value is returned. Otherwise,
* {@link Constants#CONFIG_VIEWFS_DEFAULT_MOUNT_TABLE} is returned.
*
* @param conf Configuration to use.
* @return the name of the default mount table to use.
*/
public static String getDefaultMountTableName(final Configuration conf) {
return conf.get(Constants.CONFIG_VIEWFS_DEFAULT_MOUNT_TABLE_NAME_KEY,
Constants.CONFIG_VIEWFS_DEFAULT_MOUNT_TABLE);
}
} }

View File

@ -41,12 +41,18 @@ public interface Constants {
* then the hadoop default value (/user) is used. * then the hadoop default value (/user) is used.
*/ */
public static final String CONFIG_VIEWFS_HOMEDIR = "homedir"; public static final String CONFIG_VIEWFS_HOMEDIR = "homedir";
/**
* Config key to specify the name of the default mount table.
*/
String CONFIG_VIEWFS_DEFAULT_MOUNT_TABLE_NAME_KEY =
"fs.viewfs.mounttable.default.name.key";
/** /**
* Config variable name for the default mount table. * Config variable name for the default mount table.
*/ */
public static final String CONFIG_VIEWFS_DEFAULT_MOUNT_TABLE = "default"; public static final String CONFIG_VIEWFS_DEFAULT_MOUNT_TABLE = "default";
/** /**
* Config variable full prefix for the default mount table. * Config variable full prefix for the default mount table.
*/ */

View File

@ -465,7 +465,7 @@ abstract class InodeTree<T> {
FileAlreadyExistsException, IOException { FileAlreadyExistsException, IOException {
String mountTableName = viewName; String mountTableName = viewName;
if (mountTableName == null) { if (mountTableName == null) {
mountTableName = Constants.CONFIG_VIEWFS_DEFAULT_MOUNT_TABLE; mountTableName = ConfigUtil.getDefaultMountTableName(config);
} }
homedirPrefix = ConfigUtil.getHomeDirValue(config, mountTableName); homedirPrefix = ConfigUtil.getHomeDirValue(config, mountTableName);

View File

@ -48,10 +48,9 @@ public class TestViewFsWithAuthorityLocalFs extends ViewFsBaseTest {
fcTarget = FileContext.getLocalFSFileContext(); fcTarget = FileContext.getLocalFSFileContext();
super.setUp(); // this sets up conf (and fcView which we replace) super.setUp(); // this sets up conf (and fcView which we replace)
// Now create a viewfs using a mount table called "default" // Now create a viewfs using a mount table using the {MOUNT_TABLE_NAME}
// hence viewfs://default/
schemeWithAuthority = schemeWithAuthority =
new URI(FsConstants.VIEWFS_SCHEME, "default", "/", null, null); new URI(FsConstants.VIEWFS_SCHEME, MOUNT_TABLE_NAME, "/", null, null);
fcView = FileContext.getFileContext(schemeWithAuthority, conf); fcView = FileContext.getFileContext(schemeWithAuthority, conf);
} }

View File

@ -96,6 +96,8 @@ import org.junit.Test;
* </p> * </p>
*/ */
abstract public class ViewFsBaseTest { abstract public class ViewFsBaseTest {
protected static final String MOUNT_TABLE_NAME = "mycluster";
FileContext fcView; // the view file system - the mounts are here FileContext fcView; // the view file system - the mounts are here
FileContext fcTarget; // the target file system - the mount will point here FileContext fcTarget; // the target file system - the mount will point here
Path targetTestRoot; Path targetTestRoot;
@ -129,6 +131,9 @@ abstract public class ViewFsBaseTest {
// Set up the defaultMT in the config with our mount point links // Set up the defaultMT in the config with our mount point links
conf = new Configuration(); conf = new Configuration();
conf.set(
Constants.CONFIG_VIEWFS_DEFAULT_MOUNT_TABLE_NAME_KEY,
MOUNT_TABLE_NAME);
ConfigUtil.addLink(conf, "/targetRoot", targetTestRoot.toUri()); ConfigUtil.addLink(conf, "/targetRoot", targetTestRoot.toUri());
ConfigUtil.addLink(conf, "/user", ConfigUtil.addLink(conf, "/user",
new Path(targetTestRoot,"user").toUri()); new Path(targetTestRoot,"user").toUri());
@ -1013,9 +1018,8 @@ abstract public class ViewFsBaseTest {
userUgi.doAs(new PrivilegedExceptionAction<Object>() { userUgi.doAs(new PrivilegedExceptionAction<Object>() {
@Override @Override
public Object run() throws Exception { public Object run() throws Exception {
String clusterName = Constants.CONFIG_VIEWFS_DEFAULT_MOUNT_TABLE; URI viewFsUri = new URI(
URI viewFsUri = FsConstants.VIEWFS_SCHEME, MOUNT_TABLE_NAME, "/", null, null);
new URI(FsConstants.VIEWFS_SCHEME, clusterName, "/", null, null);
FileSystem vfs = FileSystem.get(viewFsUri, conf); FileSystem vfs = FileSystem.get(viewFsUri, conf);
LambdaTestUtils.intercept(IOException.class, LambdaTestUtils.intercept(IOException.class,
"There is no primary group for UGI", () -> vfs "There is no primary group for UGI", () -> vfs

View File

@ -153,7 +153,7 @@ public class ViewFsTestSetup {
String prefix = String prefix =
new StringBuilder(Constants.CONFIG_VIEWFS_PREFIX).append(".") new StringBuilder(Constants.CONFIG_VIEWFS_PREFIX).append(".")
.append((mountTable == null .append((mountTable == null
? Constants.CONFIG_VIEWFS_DEFAULT_MOUNT_TABLE ? ConfigUtil.getDefaultMountTableName(conf)
: mountTable)) : mountTable))
.append(".").toString(); .append(".").toString();
out.writeBytes("<configuration>"); out.writeBytes("<configuration>");

View File

@ -150,6 +150,39 @@ DFSAdmin commands with View File System Overload Scheme
Please refer to the [HDFSCommands Guide](./HDFSCommands.html#dfsadmin_with_ViewFsOverloadScheme) Please refer to the [HDFSCommands Guide](./HDFSCommands.html#dfsadmin_with_ViewFsOverloadScheme)
Accessing paths without authority
---------------------------------
Accessing paths like `hdfs:///foo/bar`, `hdfs:/foo/bar` or `viewfs:/foo/bar`, where the authority (cluster name or hostname) of the path is not specified, is very common.
This is especially true when the same code is expected to run on multiple clusters with different names or HDFS Namenodes.
When `ViewFileSystemOverloadScheme` is used (as described above), and if (a) the scheme of the path being accessed is different from the scheme of the path specified as `fs.defaultFS`
and (b) if the path doesn't have an authority specified, accessing the path can result in an error like `Empty Mount table in config for viewfs://default/`.
For example, when the following configuration is used but a path like `viewfs:/foo/bar` or `viewfs:///foo/bar` is accessed, such an error arises.
```xml
<property>
<name>fs.hdfs.impl</name>
<value>org.apache.hadoop.fs.viewfs.ViewFileSystemOverloadScheme</value>
</property>
<property>
<name>fs.defaultFS</name>
<value>hdfs://cluster/</value>
</property>
```
#### Solution
To avoid the above problem, the configuration `fs.viewfs.mounttable.default.name.key` has to be set to the name of the cluster, i.e, the following should be added to `core-site.xml`
```xml
<property>
<name>fs.viewfs.mounttable.default.name.key</name>
<value>cluster</value>
</property>
```
The string in this configuration `cluster` should match the name of the authority in the value of `fs.defaultFS`. Further, the configuration should have a mount table
configured correctly as in the above examples, i.e., the configurations `fs.viewfs.mounttable.*cluster*.link.<mountLinkPath>` should be set (note the same string
`cluster` is used in these configurations).
Appendix: A Mount Table Configuration with XInclude Appendix: A Mount Table Configuration with XInclude
--------------------------------------------------- ---------------------------------------------------

View File

@ -299,14 +299,16 @@ public class TestViewFileSystemHdfs extends ViewFileSystemBaseTest {
new URI(uri2.getScheme(), uri2.getAuthority(), "/", null, null) new URI(uri2.getScheme(), uri2.getAuthority(), "/", null, null)
}; };
String clusterName = "mycluster";
final Configuration testConf = new Configuration(conf); final Configuration testConf = new Configuration(conf);
testConf.set(Constants.CONFIG_VIEWFS_DEFAULT_MOUNT_TABLE_NAME_KEY,
clusterName);
testConf.setInt(IPC_CLIENT_CONNECT_MAX_RETRIES_KEY, 1); testConf.setInt(IPC_CLIENT_CONNECT_MAX_RETRIES_KEY, 1);
final String testString = "Hello Nfly!"; final String testString = "Hello Nfly!";
final Path nflyRoot = new Path("/nflyroot"); final Path nflyRoot = new Path("/nflyroot");
ConfigUtil.addLinkNfly(testConf, ConfigUtil.addLinkNfly(testConf,
Constants.CONFIG_VIEWFS_DEFAULT_MOUNT_TABLE, clusterName,
nflyRoot.toString(), nflyRoot.toString(),
"minReplication=2," + repairKey + "=true", testUris); "minReplication=2," + repairKey + "=true", testUris);

View File

@ -17,15 +17,14 @@
*/ */
package org.apache.hadoop.fs.viewfs; package org.apache.hadoop.fs.viewfs;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.CommonConfigurationKeys; import org.apache.hadoop.fs.CommonConfigurationKeys;
import org.apache.hadoop.fs.CommonConfigurationKeysPublic; import org.apache.hadoop.fs.CommonConfigurationKeysPublic;
@ -47,6 +46,9 @@ import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.*;
/** /**
* Tests ViewFileSystemOverloadScheme with configured mount links. * Tests ViewFileSystemOverloadScheme with configured mount links.
*/ */
@ -236,6 +238,64 @@ public class TestViewFileSystemOverloadSchemeWithHdfsScheme {
} }
} }
/**
* Create mount links as follows
* hdfs://localhost:xxx/HDFSUser --> hdfs://localhost:xxx/HDFSUser/
* hdfs://localhost:xxx/local --> file://TEST_ROOT_DIR/root/
* Check that "viewfs:/" paths without authority can work when the
* default mount table name is set correctly.
*/
@Test
public void testAccessViewFsPathWithoutAuthority() throws Exception {
final Path hdfsTargetPath = new Path(defaultFSURI + HDFS_USER_FOLDER);
addMountLinks(defaultFSURI.getAuthority(),
new String[] {HDFS_USER_FOLDER, LOCAL_FOLDER },
new String[] {hdfsTargetPath.toUri().toString(),
localTargetDir.toURI().toString() },
conf);
// /HDFSUser/test
Path hdfsDir = new Path(HDFS_USER_FOLDER, "test");
// /local/test
Path localDir = new Path(LOCAL_FOLDER, "test");
FileStatus[] expectedStatus;
try (FileSystem fs = FileSystem.get(conf)) {
fs.mkdirs(hdfsDir); // /HDFSUser/test
fs.mkdirs(localDir); // /local/test
expectedStatus = fs.listStatus(new Path("/"));
}
// check for viewfs path without authority
Path viewFsRootPath = new Path("viewfs:/");
try {
viewFsRootPath.getFileSystem(conf);
Assert.fail(
"Mount table with authority default should not be initialized");
} catch (IOException e) {
assertTrue(e.getMessage().contains(
"Empty Mount table in config for viewfs://default/"));
}
// set the name of the default mount table here and
// subsequent calls should succeed.
conf.set(Constants.CONFIG_VIEWFS_DEFAULT_MOUNT_TABLE_NAME_KEY,
defaultFSURI.getAuthority());
try (FileSystem fs = viewFsRootPath.getFileSystem(conf)) {
FileStatus[] status = fs.listStatus(viewFsRootPath);
// compare only the final components of the paths as
// full paths have different schemes (hdfs:/ vs. viewfs:/).
List<String> expectedPaths = Arrays.stream(expectedStatus)
.map(s -> s.getPath().getName()).sorted()
.collect(Collectors.toList());
List<String> paths = Arrays.stream(status)
.map(s -> s.getPath().getName()).sorted()
.collect(Collectors.toList());
assertEquals(expectedPaths, paths);
}
}
/** /**
* Create mount links as follows hdfs://localhost:xxx/HDFSUser --> * Create mount links as follows hdfs://localhost:xxx/HDFSUser -->
* hdfs://localhost:xxx/HDFSUser/ hdfs://localhost:xxx/local --> * hdfs://localhost:xxx/HDFSUser/ hdfs://localhost:xxx/local -->