Skip to content

Commit a694380

Browse files
1 parent d72cdf7 commit a694380

File tree

4 files changed

+98
-1
lines changed

4 files changed

+98
-1
lines changed

hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/Constants.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1360,4 +1360,22 @@ private Constants() {
13601360
* Value: {@value}.
13611361
*/
13621362
public static final boolean OPTIMIZED_COPY_FROM_LOCAL_DEFAULT = true;
1363+
1364+
1365+
/**
1366+
* To use the same classloader that loaded S3AFileSystem to al
1367+
* load the user extensions, such as {{fs.s3a.aws.credentials.provider}}.
1368+
* It is useful to turn this off for Apache Spark applications that
1369+
* might load S3AFileSystem from the Spark distribution (Launcher classloader)
1370+
* while users might want to provide custom extensions (loaded by Spark MutableClassloader).
1371+
* Default value: true.
1372+
*/
1373+
public static final String AWS_S3_EXTENSIONS_ISOLATED_CLASSLOADER =
1374+
"fs.s3a.extensions.isolated.classloader";
1375+
1376+
1377+
/**
1378+
* Default value for {{fs.s3a.extensions.isolated.classloader}}.
1379+
*/
1380+
public static final boolean DEFAULT_AWS_S3_EXTENSIONS_ISOLATED_CLASSLOADER = true;
13631381
}

hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/S3AFileSystem.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@ public void initialize(URI name, Configuration originalConf)
534534
// fix up the classloader of the configuration to be whatever
535535
// classloader loaded this filesystem.
536536
// See: HADOOP-17372
537-
conf.setClassLoader(this.getClass().getClassLoader());
537+
isolateClassloader(conf, this.getClass().getClassLoader());
538538

539539
// patch the Hadoop security providers
540540
patchSecurityCredentialProviders(conf);
@@ -718,6 +718,18 @@ public void initialize(URI name, Configuration originalConf)
718718
}
719719
}
720720

721+
void isolateClassloader(Configuration conf, ClassLoader classLoader) {
722+
if (conf.getBoolean(Constants.AWS_S3_EXTENSIONS_ISOLATED_CLASSLOADER,
723+
Constants.DEFAULT_AWS_S3_EXTENSIONS_ISOLATED_CLASSLOADER)) {
724+
LOG.debug("Configuration classloader set to S3AFileSystem classloader: {}", classLoader);
725+
conf.setClassLoader(classLoader);
726+
} else {
727+
LOG.debug("Configuration classloader not changed, support classes needed will be loaded " +
728+
"from the classloader that instantiated the Configuration object: {}",
729+
conf.getClassLoader());
730+
}
731+
}
732+
721733
/**
722734
* Populates the configurations related to vectored IO operation
723735
* in the context which has to passed down to input streams.

hadoop-tools/hadoop-aws/src/site/markdown/tools/hadoop-aws/index.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,30 @@ obtain the credentials needed to access AWS services in the role the EC2 VM
561561
was deployed as.
562562
This AWS credential provider is enabled in S3A by default.
563563

564+
## Custom AWS Credential Providers and Apache Spark
565+
566+
Apache Spark employs two class loaders, one that loads "distribution" (Spark + Hadoop) classes and one that
567+
loads custom user classes. If the user wants to load custom implementations of AWS Credential Providers
568+
through user provided jars will need to set the following configuration:
569+
570+
```xml
571+
<property>
572+
<name>fs.s3a.extensions.isolated.classloader</name>
573+
<value>false</value>
574+
</property>
575+
<property>
576+
<name>fs.s3a.aws.credentials.provider</name>
577+
<value>CustomCredentialsProvider</value>
578+
</property>
579+
580+
```
581+
582+
If the following property is not set or set to true, the following exception will be thrown:
583+
584+
```
585+
java.io.IOException: From option fs.s3a.aws.credentials.provider java.lang.ClassNotFoundException: Class CustomCredentialsProvider not found
586+
```
587+
564588

565589
## <a name="hadoop_credential_providers"></a>Storing secrets with Hadoop Credential Providers
566590

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package org.apache.hadoop.fs.s3a;
2+
3+
import java.io.IOException;
4+
import org.apache.hadoop.conf.Configuration;
5+
import org.junit.Assert;
6+
import org.junit.Test;
7+
8+
public class TestS3AFileSystemIsolatedClassloader {
9+
10+
public static class CustomClassLoader extends ClassLoader {
11+
}
12+
13+
private final ClassLoader classLoader = new CustomClassLoader();
14+
15+
@Test
16+
public void isolatedClasspath() throws IOException {
17+
try (S3AFileSystem fs = new S3AFileSystem()) {
18+
Configuration conf = new Configuration();
19+
conf.setBoolean(Constants.AWS_S3_EXTENSIONS_ISOLATED_CLASSLOADER, true);
20+
fs.isolateClassloader(conf, classLoader);
21+
Assert.assertTrue(conf.getClassLoader() instanceof CustomClassLoader);
22+
}
23+
}
24+
25+
@Test
26+
public void isolatedClasspathDefault() throws IOException {
27+
try (S3AFileSystem fs = new S3AFileSystem()) {
28+
Configuration conf = new Configuration();
29+
fs.isolateClassloader(conf, classLoader);
30+
Assert.assertTrue(conf.getClassLoader() instanceof CustomClassLoader);
31+
}
32+
}
33+
34+
@Test
35+
public void notIsolatedClasspath() throws IOException {
36+
try (S3AFileSystem fs = new S3AFileSystem()) {
37+
Configuration conf = new Configuration();
38+
conf.setBoolean(Constants.AWS_S3_EXTENSIONS_ISOLATED_CLASSLOADER, false);
39+
fs.isolateClassloader(conf, classLoader);
40+
Assert.assertFalse(conf.getClassLoader() instanceof CustomClassLoader);
41+
}
42+
}
43+
}

0 commit comments

Comments
 (0)