From 700c1c878ea7ff7b5209ce158fe04bb66f414f04 Mon Sep 17 00:00:00 2001 From: Jason Han Date: Tue, 23 Jan 2024 13:00:56 -0800 Subject: [PATCH 01/13] [S3AG] update AWS SDK to v2.23.7 --- LICENSE-binary | 2 +- hadoop-project/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE-binary b/LICENSE-binary index 93e38cc34ee4c..685fde2611c77 100644 --- a/LICENSE-binary +++ b/LICENSE-binary @@ -363,7 +363,7 @@ org.objenesis:objenesis:2.6 org.xerial.snappy:snappy-java:1.1.10.4 org.yaml:snakeyaml:2.0 org.wildfly.openssl:wildfly-openssl:1.1.3.Final -software.amazon.awssdk:bundle:jar:2.23.5 +software.amazon.awssdk:bundle:jar:2.23.7 -------------------------------------------------------------------------------- diff --git a/hadoop-project/pom.xml b/hadoop-project/pom.xml index 1a135ded88098..fbbf8a1e2a80e 100644 --- a/hadoop-project/pom.xml +++ b/hadoop-project/pom.xml @@ -187,7 +187,7 @@ 1.0-beta-1 900 1.12.599 - 2.23.5 + 2.23.7 1.0.1 2.7.1 1.11.2 From e63c165f25c3ad7f88eab7709047f30c76f43397 Mon Sep 17 00:00:00 2001 From: Jason Han Date: Wed, 24 Jan 2024 10:59:02 -0800 Subject: [PATCH 02/13] [S3AG] Add S3AG support --- hadoop-tools/hadoop-aws/pom.xml | 10 ++ .../org/apache/hadoop/fs/s3a/Constants.java | 16 +++ .../hadoop/fs/s3a/DefaultS3ClientFactory.java | 18 ++++ .../s3a/TestS3AccessGrantConfiguration.java | 100 ++++++++++++++++++ 4 files changed, 144 insertions(+) create mode 100644 hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/TestS3AccessGrantConfiguration.java diff --git a/hadoop-tools/hadoop-aws/pom.xml b/hadoop-tools/hadoop-aws/pom.xml index 5a0f2356b5e4a..9882ad11ec8fc 100644 --- a/hadoop-tools/hadoop-aws/pom.xml +++ b/hadoop-tools/hadoop-aws/pom.xml @@ -508,6 +508,16 @@ bundle compile + + software.amazon.s3.accessgrants + aws-s3-accessgrants-java-plugin + 2.0.0 + + + software.amazon.eventstream + eventstream + test + org.assertj assertj-core diff --git a/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/Constants.java b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/Constants.java index 96dc2be6a260d..c1d3eae761294 100644 --- a/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/Constants.java +++ b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/Constants.java @@ -1624,4 +1624,20 @@ private Constants() { * Value: {@value}. */ public static final boolean DEFAULT_AWS_S3_CLASSLOADER_ISOLATION = true; + + /** + * Flag to enable S3 Access Grants to control authorization to S3 data. More information: + * https://aws.amazon.com/s3/features/access-grants/ + * and + * https://github.com/aws/aws-s3-accessgrants-plugin-java-v2/ + */ + public static final String AWS_S3_ACCESS_GRANTS_ENABLED = "fs.s3a.access-grants.enabled"; + + /** + * Flag to enable jobs fall back to the Job Execution IAM role in + * case they get Access Denied from the S3 Access Grants call. More information: + * https://github.com/aws/aws-s3-accessgrants-plugin-java-v2/ + */ + public static final String AWS_S3_ACCESS_GRANTS_FALLBACK_TO_IAM_ENABLED = + "fs.s3a.access-grants.fallback-to-iam"; } diff --git a/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/DefaultS3ClientFactory.java b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/DefaultS3ClientFactory.java index 284ba8e6ae5c9..7994ea274854e 100644 --- a/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/DefaultS3ClientFactory.java +++ b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/DefaultS3ClientFactory.java @@ -37,6 +37,7 @@ import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient; import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity; import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.s3accessgrants.plugin.S3AccessGrantsPlugin; import software.amazon.awssdk.services.s3.S3AsyncClient; import software.amazon.awssdk.services.s3.S3BaseClientBuilder; import software.amazon.awssdk.services.s3.S3Client; @@ -53,6 +54,8 @@ import org.apache.hadoop.fs.store.LogExactlyOnce; import static org.apache.hadoop.fs.s3a.Constants.AWS_REGION; +import static org.apache.hadoop.fs.s3a.Constants.AWS_S3_ACCESS_GRANTS_ENABLED; +import static org.apache.hadoop.fs.s3a.Constants.AWS_S3_ACCESS_GRANTS_FALLBACK_TO_IAM_ENABLED; import static org.apache.hadoop.fs.s3a.Constants.AWS_S3_DEFAULT_REGION; import static org.apache.hadoop.fs.s3a.Constants.CENTRAL_ENDPOINT; import static org.apache.hadoop.fs.s3a.Constants.FIPS_ENDPOINT; @@ -178,6 +181,8 @@ private , ClientT> Build configureEndpointAndRegion(builder, parameters, conf); + applyS3AccessGrantsConfigurations(builder, conf); + S3Configuration serviceConfiguration = S3Configuration.builder() .pathStyleAccessEnabled(parameters.isPathStyleAccess()) .checksumValidationEnabled(parameters.isChecksumValidationEnabled()) @@ -401,4 +406,17 @@ private static Region getS3RegionFromEndpoint(final String endpoint, return Region.of(AWS_S3_DEFAULT_REGION); } + public static , ClientT> void + applyS3AccessGrantsConfigurations(BuilderT builder, Configuration conf) { + boolean s3agEnabled = conf.getBoolean(AWS_S3_ACCESS_GRANTS_ENABLED, false); + if (s3agEnabled) { + boolean s3agFallbackEnabled = conf.getBoolean(AWS_S3_ACCESS_GRANTS_FALLBACK_TO_IAM_ENABLED, false); + S3AccessGrantsPlugin accessGrantsPlugin = + S3AccessGrantsPlugin.builder().enableFallback(s3agFallbackEnabled).build(); + builder.addPlugin(accessGrantsPlugin); + LOG.info("s3ag plugin is added to s3 client with fallback: {}", s3agFallbackEnabled); + } else { + LOG.debug("s3ag plugin is not added to s3 client."); + } + } } diff --git a/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/TestS3AccessGrantConfiguration.java b/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/TestS3AccessGrantConfiguration.java new file mode 100644 index 0000000000000..4050f29223111 --- /dev/null +++ b/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/TestS3AccessGrantConfiguration.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.fs.s3a; + +import org.apache.hadoop.conf.Configuration; +import org.junit.Test; + +import software.amazon.awssdk.services.s3.S3AsyncClientBuilder; +import software.amazon.awssdk.services.s3.S3AsyncClient; +import software.amazon.awssdk.services.s3.S3BaseClientBuilder; +import software.amazon.awssdk.services.s3.S3ClientBuilder; +import software.amazon.awssdk.services.s3.S3Client; + +import static org.apache.hadoop.fs.s3a.Constants.AWS_S3_ACCESS_GRANTS_ENABLED; +import static org.junit.Assert.assertEquals; + + +/** + * Test S3 Access Grants configurations + */ +public class TestS3AccessGrantConfiguration { + + @Test + public void testS3AccessGrantsEnabled() { + Configuration conf = new Configuration(); + conf.set(AWS_S3_ACCESS_GRANTS_ENABLED, "true"); + S3ClientBuilder builder = S3Client.builder(); + DefaultS3ClientFactory.applyS3AccessGrantsConfigurations(builder, conf); + verifyS3AGPluginEnabled(builder); + } + + @Test + public void testS3AccessGrantsEnabledAsync() { + Configuration conf = new Configuration(); + conf.set(AWS_S3_ACCESS_GRANTS_ENABLED, "true"); + S3AsyncClientBuilder builder = S3AsyncClient.builder(); + DefaultS3ClientFactory.applyS3AccessGrantsConfigurations(builder, conf); + verifyS3AGPluginEnabled(builder); + } + + @Test + public void testS3AccessGrantsDisabled() { + Configuration conf = new Configuration(); + conf.set(AWS_S3_ACCESS_GRANTS_ENABLED, "false"); + S3ClientBuilder builder = S3Client.builder(); + DefaultS3ClientFactory.applyS3AccessGrantsConfigurations(builder, conf); + verifyS3AGPluginDisabled(builder); + } + + @Test + public void testS3AccessGrantsDisabledByDefault() { + Configuration conf = new Configuration(); + S3ClientBuilder builder = S3Client.builder(); + DefaultS3ClientFactory.applyS3AccessGrantsConfigurations(builder, conf); + verifyS3AGPluginDisabled(builder); + } + + @Test + public void testS3AccessGrantsDisabledAsync() { + Configuration conf = new Configuration(); + conf.set(AWS_S3_ACCESS_GRANTS_ENABLED, "false"); + S3AsyncClientBuilder builder = S3AsyncClient.builder(); + DefaultS3ClientFactory.applyS3AccessGrantsConfigurations(builder, conf); + verifyS3AGPluginDisabled(builder); + } + + @Test + public void testS3AccessGrantsDisabledByDefaultAsync() { + Configuration conf = new Configuration(); + S3AsyncClientBuilder builder = S3AsyncClient.builder(); + DefaultS3ClientFactory.applyS3AccessGrantsConfigurations(builder, conf); + verifyS3AGPluginDisabled(builder); + } + + private , ClientT> void verifyS3AGPluginEnabled(BuilderT builder) { + assertEquals(builder.plugins().size(), 1); + assertEquals(builder.plugins().get(0).getClass().getName(), + "software.amazon.awssdk.s3accessgrants.plugin.S3AccessGrantsPlugin"); + } + + private , ClientT> void verifyS3AGPluginDisabled(BuilderT builder) { + assertEquals(builder.plugins().size(),0); + } +} \ No newline at end of file From 2e7075949820bf7a422da85449ccf8006a5400bb Mon Sep 17 00:00:00 2001 From: Jason Han Date: Thu, 25 Jan 2024 11:26:39 -0800 Subject: [PATCH 03/13] [S3AG] Format code --- .../apache/hadoop/fs/s3a/DefaultS3ClientFactory.java | 5 +++-- .../hadoop/fs/s3a/TestS3AccessGrantConfiguration.java | 10 ++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/DefaultS3ClientFactory.java b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/DefaultS3ClientFactory.java index 7994ea274854e..0e90b8d017dd6 100644 --- a/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/DefaultS3ClientFactory.java +++ b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/DefaultS3ClientFactory.java @@ -407,10 +407,11 @@ private static Region getS3RegionFromEndpoint(final String endpoint, } public static , ClientT> void - applyS3AccessGrantsConfigurations(BuilderT builder, Configuration conf) { + applyS3AccessGrantsConfigurations(BuilderT builder, Configuration conf) { boolean s3agEnabled = conf.getBoolean(AWS_S3_ACCESS_GRANTS_ENABLED, false); if (s3agEnabled) { - boolean s3agFallbackEnabled = conf.getBoolean(AWS_S3_ACCESS_GRANTS_FALLBACK_TO_IAM_ENABLED, false); + boolean s3agFallbackEnabled = conf.getBoolean( + AWS_S3_ACCESS_GRANTS_FALLBACK_TO_IAM_ENABLED, false); S3AccessGrantsPlugin accessGrantsPlugin = S3AccessGrantsPlugin.builder().enableFallback(s3agFallbackEnabled).build(); builder.addPlugin(accessGrantsPlugin); diff --git a/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/TestS3AccessGrantConfiguration.java b/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/TestS3AccessGrantConfiguration.java index 4050f29223111..89c31f2e11116 100644 --- a/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/TestS3AccessGrantConfiguration.java +++ b/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/TestS3AccessGrantConfiguration.java @@ -32,7 +32,7 @@ /** - * Test S3 Access Grants configurations + * Test S3 Access Grants configurations. */ public class TestS3AccessGrantConfiguration { @@ -88,13 +88,15 @@ public void testS3AccessGrantsDisabledByDefaultAsync() { verifyS3AGPluginDisabled(builder); } - private , ClientT> void verifyS3AGPluginEnabled(BuilderT builder) { + private , ClientT> void + verifyS3AGPluginEnabled(BuilderT builder) { assertEquals(builder.plugins().size(), 1); assertEquals(builder.plugins().get(0).getClass().getName(), "software.amazon.awssdk.s3accessgrants.plugin.S3AccessGrantsPlugin"); } - private , ClientT> void verifyS3AGPluginDisabled(BuilderT builder) { - assertEquals(builder.plugins().size(),0); + private , ClientT> void + verifyS3AGPluginDisabled(BuilderT builder) { + assertEquals(builder.plugins().size(), 0); } } \ No newline at end of file From 70842d90861e895a15345d971eb25fbf4ad2e799 Mon Sep 17 00:00:00 2001 From: Jason Han Date: Tue, 30 Jan 2024 17:57:04 -0800 Subject: [PATCH 04/13] [S3AG] remove unintended change --- hadoop-tools/hadoop-aws/pom.xml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/hadoop-tools/hadoop-aws/pom.xml b/hadoop-tools/hadoop-aws/pom.xml index 9882ad11ec8fc..246e81d36c6cb 100644 --- a/hadoop-tools/hadoop-aws/pom.xml +++ b/hadoop-tools/hadoop-aws/pom.xml @@ -513,11 +513,6 @@ aws-s3-accessgrants-java-plugin 2.0.0 - - software.amazon.eventstream - eventstream - test - org.assertj assertj-core From 08aaf66c84b3acab4666771b68a86a6aa9728449 Mon Sep 17 00:00:00 2001 From: Jason Han Date: Tue, 30 Jan 2024 19:37:48 -0800 Subject: [PATCH 05/13] [S3AG] resolve dependency --- hadoop-tools/hadoop-aws/pom.xml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/hadoop-tools/hadoop-aws/pom.xml b/hadoop-tools/hadoop-aws/pom.xml index 246e81d36c6cb..c747398acbc29 100644 --- a/hadoop-tools/hadoop-aws/pom.xml +++ b/hadoop-tools/hadoop-aws/pom.xml @@ -512,6 +512,24 @@ software.amazon.s3.accessgrants aws-s3-accessgrants-java-plugin 2.0.0 + + + software.amazon.awssdk + * + + + com.github.ben-manes.caffeine + * + + + org.apache.logging.log4j + * + + + org.assertj + * + + org.assertj From 4c00a135b1d5f6a0a533db22c3b5a0b3707be2e4 Mon Sep 17 00:00:00 2001 From: Jason Han Date: Thu, 1 Feb 2024 13:11:11 -0800 Subject: [PATCH 06/13] Revert "[S3AG] update AWS SDK to v2.23.7" This reverts commit 1e47cc95255afb7b6d7520e21222891a6da70d69. --- LICENSE-binary | 2 +- hadoop-project/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE-binary b/LICENSE-binary index 685fde2611c77..93e38cc34ee4c 100644 --- a/LICENSE-binary +++ b/LICENSE-binary @@ -363,7 +363,7 @@ org.objenesis:objenesis:2.6 org.xerial.snappy:snappy-java:1.1.10.4 org.yaml:snakeyaml:2.0 org.wildfly.openssl:wildfly-openssl:1.1.3.Final -software.amazon.awssdk:bundle:jar:2.23.7 +software.amazon.awssdk:bundle:jar:2.23.5 -------------------------------------------------------------------------------- diff --git a/hadoop-project/pom.xml b/hadoop-project/pom.xml index fbbf8a1e2a80e..1a135ded88098 100644 --- a/hadoop-project/pom.xml +++ b/hadoop-project/pom.xml @@ -187,7 +187,7 @@ 1.0-beta-1 900 1.12.599 - 2.23.7 + 2.23.5 1.0.1 2.7.1 1.11.2 From b765ff9b0620935d734d74aa2db98952e81fd582 Mon Sep 17 00:00:00 2001 From: Jason Han Date: Thu, 1 Feb 2024 15:05:50 -0800 Subject: [PATCH 07/13] [S3AG] make plugin optional --- hadoop-tools/hadoop-aws/pom.xml | 1 + .../org/apache/hadoop/fs/s3a/Constants.java | 9 +-- .../hadoop/fs/s3a/DefaultS3ClientFactory.java | 41 ++++++++++- .../s3a/TestS3AccessGrantConfiguration.java | 69 +++++++++---------- 4 files changed, 75 insertions(+), 45 deletions(-) diff --git a/hadoop-tools/hadoop-aws/pom.xml b/hadoop-tools/hadoop-aws/pom.xml index c747398acbc29..98ff520a5d7a5 100644 --- a/hadoop-tools/hadoop-aws/pom.xml +++ b/hadoop-tools/hadoop-aws/pom.xml @@ -512,6 +512,7 @@ software.amazon.s3.accessgrants aws-s3-accessgrants-java-plugin 2.0.0 + provided software.amazon.awssdk diff --git a/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/Constants.java b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/Constants.java index c1d3eae761294..bbc57cff9ed60 100644 --- a/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/Constants.java +++ b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/Constants.java @@ -1626,18 +1626,19 @@ private Constants() { public static final boolean DEFAULT_AWS_S3_CLASSLOADER_ISOLATION = true; /** - * Flag to enable S3 Access Grants to control authorization to S3 data. More information: + * Flag {@value} + * to enable S3 Access Grants to control authorization to S3 data. More information: * https://aws.amazon.com/s3/features/access-grants/ * and * https://github.com/aws/aws-s3-accessgrants-plugin-java-v2/ */ - public static final String AWS_S3_ACCESS_GRANTS_ENABLED = "fs.s3a.access-grants.enabled"; + public static final String AWS_S3_ACCESS_GRANTS_ENABLED = "fs.s3a.accessgrants.enabled"; /** - * Flag to enable jobs fall back to the Job Execution IAM role in + * Flag {@value} to enable jobs fall back to the Job Execution IAM role in * case they get Access Denied from the S3 Access Grants call. More information: * https://github.com/aws/aws-s3-accessgrants-plugin-java-v2/ */ public static final String AWS_S3_ACCESS_GRANTS_FALLBACK_TO_IAM_ENABLED = - "fs.s3a.access-grants.fallback-to-iam"; + "fs.s3a.accessgrants.fallbacktoiam"; } diff --git a/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/DefaultS3ClientFactory.java b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/DefaultS3ClientFactory.java index 0e90b8d017dd6..ca7cae4aa67eb 100644 --- a/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/DefaultS3ClientFactory.java +++ b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/DefaultS3ClientFactory.java @@ -24,6 +24,7 @@ import org.apache.hadoop.classification.VisibleForTesting; import org.apache.hadoop.fs.s3a.impl.AWSClientConfig; +import org.apache.hadoop.fs.s3a.impl.InstantiationIOException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -67,6 +68,7 @@ import static org.apache.hadoop.fs.s3a.Constants.AWS_SERVICE_IDENTIFIER_S3; import static org.apache.hadoop.fs.s3a.auth.SignerFactory.createHttpSigner; import static org.apache.hadoop.fs.s3a.impl.AWSHeaders.REQUESTER_PAYS_HEADER; +import static org.apache.hadoop.fs.s3a.impl.InstantiationIOException.unavailable; import static org.apache.hadoop.fs.s3a.impl.InternalConstants.AUTH_SCHEME_AWS_SIGV_4; import static org.apache.hadoop.util.Preconditions.checkArgument; @@ -91,6 +93,8 @@ public class DefaultS3ClientFactory extends Configured protected static final Logger LOG = LoggerFactory.getLogger(DefaultS3ClientFactory.class); + private static final LogExactlyOnce LOG_EXACTLY_ONCE = new LogExactlyOnce(LOG); + /** * A one-off warning of default region chains in use. */ @@ -115,6 +119,34 @@ public class DefaultS3ClientFactory extends Configured public static final String ERROR_ENDPOINT_WITH_FIPS = "An endpoint cannot set when " + FIPS_ENDPOINT + " is true"; + private static final String S3AG_PLUGIN_CLASSNAME = + "software.amazon.awssdk.s3accessgrants.plugin.S3AccessGrantsPlugin"; + + /** + * S3 Access Grants plugin availability. + */ + private static final boolean S3AG_PLUGIN_FOUND = checkForS3AGPlugin(); + + private static boolean checkForS3AGPlugin() { + try { + ClassLoader cl = DefaultS3ClientFactory.class.getClassLoader(); + cl.loadClass(S3AG_PLUGIN_CLASSNAME); + LOG.debug("S3AG plugin class {} found", S3AG_PLUGIN_CLASSNAME); + return true; + } catch (Exception e) { + LOG.debug("S3AG plugin class {} not found", S3AG_PLUGIN_CLASSNAME, e); + return false; + } + } + + /** + * Is the Encryption client available? + * @return true if it was found in the classloader + */ + private static synchronized boolean isS3AGPluginAvailable() { + return S3AG_PLUGIN_FOUND; + } + @Override public S3Client createS3Client( final URI uri, @@ -407,17 +439,20 @@ private static Region getS3RegionFromEndpoint(final String endpoint, } public static , ClientT> void - applyS3AccessGrantsConfigurations(BuilderT builder, Configuration conf) { + applyS3AccessGrantsConfigurations(BuilderT builder, Configuration conf) throws InstantiationIOException { boolean s3agEnabled = conf.getBoolean(AWS_S3_ACCESS_GRANTS_ENABLED, false); if (s3agEnabled) { + if (!isS3AGPluginAvailable()) { + throw unavailable(null, S3AG_PLUGIN_CLASSNAME, null, "No S3AG plugin available"); + } boolean s3agFallbackEnabled = conf.getBoolean( AWS_S3_ACCESS_GRANTS_FALLBACK_TO_IAM_ENABLED, false); S3AccessGrantsPlugin accessGrantsPlugin = S3AccessGrantsPlugin.builder().enableFallback(s3agFallbackEnabled).build(); builder.addPlugin(accessGrantsPlugin); - LOG.info("s3ag plugin is added to s3 client with fallback: {}", s3agFallbackEnabled); + LOG_EXACTLY_ONCE.info("s3ag plugin is added to s3 client with fallback: {}", s3agFallbackEnabled); } else { - LOG.debug("s3ag plugin is not added to s3 client."); + LOG_EXACTLY_ONCE.debug("s3ag plugin is not added to s3 client."); } } } diff --git a/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/TestS3AccessGrantConfiguration.java b/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/TestS3AccessGrantConfiguration.java index 89c31f2e11116..c2c6290d4f3a7 100644 --- a/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/TestS3AccessGrantConfiguration.java +++ b/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/TestS3AccessGrantConfiguration.java @@ -19,12 +19,13 @@ package org.apache.hadoop.fs.s3a; import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.s3a.impl.InstantiationIOException; +import org.apache.hadoop.test.AbstractHadoopTestBase; +import org.junit.Assert; import org.junit.Test; -import software.amazon.awssdk.services.s3.S3AsyncClientBuilder; import software.amazon.awssdk.services.s3.S3AsyncClient; import software.amazon.awssdk.services.s3.S3BaseClientBuilder; -import software.amazon.awssdk.services.s3.S3ClientBuilder; import software.amazon.awssdk.services.s3.S3Client; import static org.apache.hadoop.fs.s3a.Constants.AWS_S3_ACCESS_GRANTS_ENABLED; @@ -34,69 +35,61 @@ /** * Test S3 Access Grants configurations. */ -public class TestS3AccessGrantConfiguration { +public class TestS3AccessGrantConfiguration extends AbstractHadoopTestBase { @Test public void testS3AccessGrantsEnabled() { - Configuration conf = new Configuration(); - conf.set(AWS_S3_ACCESS_GRANTS_ENABLED, "true"); - S3ClientBuilder builder = S3Client.builder(); - DefaultS3ClientFactory.applyS3AccessGrantsConfigurations(builder, conf); - verifyS3AGPluginEnabled(builder); + applyVerifyS3AGPlugin(S3Client.builder(), false, true); } @Test public void testS3AccessGrantsEnabledAsync() { - Configuration conf = new Configuration(); - conf.set(AWS_S3_ACCESS_GRANTS_ENABLED, "true"); - S3AsyncClientBuilder builder = S3AsyncClient.builder(); - DefaultS3ClientFactory.applyS3AccessGrantsConfigurations(builder, conf); - verifyS3AGPluginEnabled(builder); + applyVerifyS3AGPlugin(S3AsyncClient.builder(), false, true); } @Test public void testS3AccessGrantsDisabled() { - Configuration conf = new Configuration(); - conf.set(AWS_S3_ACCESS_GRANTS_ENABLED, "false"); - S3ClientBuilder builder = S3Client.builder(); - DefaultS3ClientFactory.applyS3AccessGrantsConfigurations(builder, conf); - verifyS3AGPluginDisabled(builder); + applyVerifyS3AGPlugin(S3Client.builder(), false, false); } @Test public void testS3AccessGrantsDisabledByDefault() { - Configuration conf = new Configuration(); - S3ClientBuilder builder = S3Client.builder(); - DefaultS3ClientFactory.applyS3AccessGrantsConfigurations(builder, conf); - verifyS3AGPluginDisabled(builder); + applyVerifyS3AGPlugin(S3Client.builder(), true, false); } @Test public void testS3AccessGrantsDisabledAsync() { - Configuration conf = new Configuration(); - conf.set(AWS_S3_ACCESS_GRANTS_ENABLED, "false"); - S3AsyncClientBuilder builder = S3AsyncClient.builder(); - DefaultS3ClientFactory.applyS3AccessGrantsConfigurations(builder, conf); - verifyS3AGPluginDisabled(builder); + applyVerifyS3AGPlugin(S3AsyncClient.builder(), false, false); } @Test public void testS3AccessGrantsDisabledByDefaultAsync() { - Configuration conf = new Configuration(); - S3AsyncClientBuilder builder = S3AsyncClient.builder(); - DefaultS3ClientFactory.applyS3AccessGrantsConfigurations(builder, conf); - verifyS3AGPluginDisabled(builder); + applyVerifyS3AGPlugin(S3AsyncClient.builder(), true, false); } - private , ClientT> void - verifyS3AGPluginEnabled(BuilderT builder) { - assertEquals(builder.plugins().size(), 1); - assertEquals(builder.plugins().get(0).getClass().getName(), - "software.amazon.awssdk.s3accessgrants.plugin.S3AccessGrantsPlugin"); + private Configuration createConfig(boolean isDefault, boolean s3agEnabled) { + Configuration conf = new Configuration(); + if (!isDefault){ + conf.setBoolean(AWS_S3_ACCESS_GRANTS_ENABLED, s3agEnabled); + } + return conf; } private , ClientT> void - verifyS3AGPluginDisabled(BuilderT builder) { - assertEquals(builder.plugins().size(), 0); + applyVerifyS3AGPlugin(BuilderT builder, boolean isDefault, boolean enabled) { + try { + DefaultS3ClientFactory.applyS3AccessGrantsConfigurations(builder, createConfig(isDefault, enabled)); + if (enabled){ + assertEquals(builder.plugins().size(), 1); + assertEquals(builder.plugins().get(0).getClass().getName(), + "software.amazon.awssdk.s3accessgrants.plugin.S3AccessGrantsPlugin"); + } + else { + assertEquals(builder.plugins().size(), 0); + } + } catch (InstantiationIOException e) { + Assert.assertTrue(true); + } } + } \ No newline at end of file From 9cee6c88e5b668c217141b7ed5a0b79bc404590a Mon Sep 17 00:00:00 2001 From: Jason Han Date: Fri, 2 Feb 2024 11:15:40 -0800 Subject: [PATCH 08/13] [S3AG] restrict import plugin --- hadoop-tools/hadoop-aws/pom.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hadoop-tools/hadoop-aws/pom.xml b/hadoop-tools/hadoop-aws/pom.xml index 98ff520a5d7a5..50e04aa84d6e1 100644 --- a/hadoop-tools/hadoop-aws/pom.xml +++ b/hadoop-tools/hadoop-aws/pom.xml @@ -464,6 +464,16 @@ org.apache.hadoop.mapred.** + + false + Restrict S3AG plugin class imports to default client factory + + org.apache.hadoop.fs.s3a.DefaultS3ClientFactory + + + software.amazon.awssdk.s3accessgrants.plugin.S3AccessGrantsPlugin + + From ba8c41e376e71957f72c6f5273b3322c966ff06e Mon Sep 17 00:00:00 2001 From: Jason Han Date: Fri, 2 Feb 2024 11:24:07 -0800 Subject: [PATCH 09/13] [S3AG] log warning for missing plugin without exception --- .../hadoop/fs/s3a/DefaultS3ClientFactory.java | 19 +++++++++-------- .../s3a/TestS3AccessGrantConfiguration.java | 21 +++++++------------ 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/DefaultS3ClientFactory.java b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/DefaultS3ClientFactory.java index ca7cae4aa67eb..9176ba8da0b8c 100644 --- a/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/DefaultS3ClientFactory.java +++ b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/DefaultS3ClientFactory.java @@ -439,18 +439,19 @@ private static Region getS3RegionFromEndpoint(final String endpoint, } public static , ClientT> void - applyS3AccessGrantsConfigurations(BuilderT builder, Configuration conf) throws InstantiationIOException { + applyS3AccessGrantsConfigurations(BuilderT builder, Configuration conf) { boolean s3agEnabled = conf.getBoolean(AWS_S3_ACCESS_GRANTS_ENABLED, false); if (s3agEnabled) { - if (!isS3AGPluginAvailable()) { - throw unavailable(null, S3AG_PLUGIN_CLASSNAME, null, "No S3AG plugin available"); + if (isS3AGPluginAvailable()) { + boolean s3agFallbackEnabled = conf.getBoolean( + AWS_S3_ACCESS_GRANTS_FALLBACK_TO_IAM_ENABLED, false); + S3AccessGrantsPlugin accessGrantsPlugin = + S3AccessGrantsPlugin.builder().enableFallback(s3agFallbackEnabled).build(); + builder.addPlugin(accessGrantsPlugin); + LOG_EXACTLY_ONCE.info("s3ag plugin is added to s3 client with fallback: {}", s3agFallbackEnabled); + } else { + LOG_EXACTLY_ONCE.warn("s3ag plugin is not available."); } - boolean s3agFallbackEnabled = conf.getBoolean( - AWS_S3_ACCESS_GRANTS_FALLBACK_TO_IAM_ENABLED, false); - S3AccessGrantsPlugin accessGrantsPlugin = - S3AccessGrantsPlugin.builder().enableFallback(s3agFallbackEnabled).build(); - builder.addPlugin(accessGrantsPlugin); - LOG_EXACTLY_ONCE.info("s3ag plugin is added to s3 client with fallback: {}", s3agFallbackEnabled); } else { LOG_EXACTLY_ONCE.debug("s3ag plugin is not added to s3 client."); } diff --git a/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/TestS3AccessGrantConfiguration.java b/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/TestS3AccessGrantConfiguration.java index c2c6290d4f3a7..57c47d5d6e035 100644 --- a/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/TestS3AccessGrantConfiguration.java +++ b/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/TestS3AccessGrantConfiguration.java @@ -77,19 +77,14 @@ private Configuration createConfig(boolean isDefault, boolean s3agEnabled) { private , ClientT> void applyVerifyS3AGPlugin(BuilderT builder, boolean isDefault, boolean enabled) { - try { - DefaultS3ClientFactory.applyS3AccessGrantsConfigurations(builder, createConfig(isDefault, enabled)); - if (enabled){ - assertEquals(builder.plugins().size(), 1); - assertEquals(builder.plugins().get(0).getClass().getName(), - "software.amazon.awssdk.s3accessgrants.plugin.S3AccessGrantsPlugin"); - } - else { - assertEquals(builder.plugins().size(), 0); - } - } catch (InstantiationIOException e) { - Assert.assertTrue(true); + DefaultS3ClientFactory.applyS3AccessGrantsConfigurations(builder, createConfig(isDefault, enabled)); + if (enabled){ + assertEquals(builder.plugins().size(), 1); + assertEquals(builder.plugins().get(0).getClass().getName(), + "software.amazon.awssdk.s3accessgrants.plugin.S3AccessGrantsPlugin"); + } + else { + assertEquals(builder.plugins().size(), 0); } } - } \ No newline at end of file From 55c881168ab68d8d9d3e73d1d492f1ae7a23e7c0 Mon Sep 17 00:00:00 2001 From: Jason Han Date: Fri, 2 Feb 2024 19:18:05 -0800 Subject: [PATCH 10/13] [S3AG] make plugin optional --- hadoop-tools/hadoop-aws/pom.xml | 10 --- .../hadoop/fs/s3a/DefaultS3ClientFactory.java | 74 ++++++++----------- .../fs/s3a/tools/S3AccessGrantsUtil.java | 60 +++++++++++++++ .../s3a/TestS3AccessGrantConfiguration.java | 9 +-- 4 files changed, 93 insertions(+), 60 deletions(-) create mode 100644 hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/tools/S3AccessGrantsUtil.java diff --git a/hadoop-tools/hadoop-aws/pom.xml b/hadoop-tools/hadoop-aws/pom.xml index 50e04aa84d6e1..98ff520a5d7a5 100644 --- a/hadoop-tools/hadoop-aws/pom.xml +++ b/hadoop-tools/hadoop-aws/pom.xml @@ -464,16 +464,6 @@ org.apache.hadoop.mapred.** - - false - Restrict S3AG plugin class imports to default client factory - - org.apache.hadoop.fs.s3a.DefaultS3ClientFactory - - - software.amazon.awssdk.s3accessgrants.plugin.S3AccessGrantsPlugin - - diff --git a/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/DefaultS3ClientFactory.java b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/DefaultS3ClientFactory.java index 9176ba8da0b8c..b201806d70be3 100644 --- a/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/DefaultS3ClientFactory.java +++ b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/DefaultS3ClientFactory.java @@ -19,12 +19,12 @@ package org.apache.hadoop.fs.s3a; import java.io.IOException; +import java.lang.reflect.Method; import java.net.URI; import java.net.URISyntaxException; import org.apache.hadoop.classification.VisibleForTesting; import org.apache.hadoop.fs.s3a.impl.AWSClientConfig; -import org.apache.hadoop.fs.s3a.impl.InstantiationIOException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -38,7 +38,6 @@ import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient; import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity; import software.amazon.awssdk.regions.Region; -import software.amazon.awssdk.s3accessgrants.plugin.S3AccessGrantsPlugin; import software.amazon.awssdk.services.s3.S3AsyncClient; import software.amazon.awssdk.services.s3.S3BaseClientBuilder; import software.amazon.awssdk.services.s3.S3Client; @@ -56,7 +55,6 @@ import static org.apache.hadoop.fs.s3a.Constants.AWS_REGION; import static org.apache.hadoop.fs.s3a.Constants.AWS_S3_ACCESS_GRANTS_ENABLED; -import static org.apache.hadoop.fs.s3a.Constants.AWS_S3_ACCESS_GRANTS_FALLBACK_TO_IAM_ENABLED; import static org.apache.hadoop.fs.s3a.Constants.AWS_S3_DEFAULT_REGION; import static org.apache.hadoop.fs.s3a.Constants.CENTRAL_ENDPOINT; import static org.apache.hadoop.fs.s3a.Constants.FIPS_ENDPOINT; @@ -68,7 +66,6 @@ import static org.apache.hadoop.fs.s3a.Constants.AWS_SERVICE_IDENTIFIER_S3; import static org.apache.hadoop.fs.s3a.auth.SignerFactory.createHttpSigner; import static org.apache.hadoop.fs.s3a.impl.AWSHeaders.REQUESTER_PAYS_HEADER; -import static org.apache.hadoop.fs.s3a.impl.InstantiationIOException.unavailable; import static org.apache.hadoop.fs.s3a.impl.InternalConstants.AUTH_SCHEME_AWS_SIGV_4; import static org.apache.hadoop.util.Preconditions.checkArgument; @@ -119,34 +116,8 @@ public class DefaultS3ClientFactory extends Configured public static final String ERROR_ENDPOINT_WITH_FIPS = "An endpoint cannot set when " + FIPS_ENDPOINT + " is true"; - private static final String S3AG_PLUGIN_CLASSNAME = - "software.amazon.awssdk.s3accessgrants.plugin.S3AccessGrantsPlugin"; - - /** - * S3 Access Grants plugin availability. - */ - private static final boolean S3AG_PLUGIN_FOUND = checkForS3AGPlugin(); - - private static boolean checkForS3AGPlugin() { - try { - ClassLoader cl = DefaultS3ClientFactory.class.getClassLoader(); - cl.loadClass(S3AG_PLUGIN_CLASSNAME); - LOG.debug("S3AG plugin class {} found", S3AG_PLUGIN_CLASSNAME); - return true; - } catch (Exception e) { - LOG.debug("S3AG plugin class {} not found", S3AG_PLUGIN_CLASSNAME, e); - return false; - } - } - - /** - * Is the Encryption client available? - * @return true if it was found in the classloader - */ - private static synchronized boolean isS3AGPluginAvailable() { - return S3AG_PLUGIN_FOUND; - } - + private static final String S3AG_UTIL_CLASSNAME = + "org.apache.hadoop.fs.s3a.tools.S3AccessGrantsUtil"; @Override public S3Client createS3Client( final URI uri, @@ -441,19 +412,32 @@ private static Region getS3RegionFromEndpoint(final String endpoint, public static , ClientT> void applyS3AccessGrantsConfigurations(BuilderT builder, Configuration conf) { boolean s3agEnabled = conf.getBoolean(AWS_S3_ACCESS_GRANTS_ENABLED, false); - if (s3agEnabled) { - if (isS3AGPluginAvailable()) { - boolean s3agFallbackEnabled = conf.getBoolean( - AWS_S3_ACCESS_GRANTS_FALLBACK_TO_IAM_ENABLED, false); - S3AccessGrantsPlugin accessGrantsPlugin = - S3AccessGrantsPlugin.builder().enableFallback(s3agFallbackEnabled).build(); - builder.addPlugin(accessGrantsPlugin); - LOG_EXACTLY_ONCE.info("s3ag plugin is added to s3 client with fallback: {}", s3agFallbackEnabled); - } else { - LOG_EXACTLY_ONCE.warn("s3ag plugin is not available."); - } - } else { - LOG_EXACTLY_ONCE.debug("s3ag plugin is not added to s3 client."); + if (!s3agEnabled){ + LOG_EXACTLY_ONCE.debug("s3ag plugin is not enabled."); + return; + } + try { + Class s3agUtil = Class.forName(S3AG_UTIL_CLASSNAME); + Class[] argTypes = new Class[2]; + argTypes[0] = S3BaseClientBuilder.class; + argTypes[1] = Configuration.class; + Method applyS3agConfig = + s3agUtil.getMethod("applyS3AccessGrantsConfigurations", S3BaseClientBuilder.class, Configuration.class); + applyS3agConfig.invoke(null, builder, conf); + } catch (ClassNotFoundException e) { + LOG_EXACTLY_ONCE.debug( + "Class {} is not found exception: {}.", + S3AG_UTIL_CLASSNAME, + e.getStackTrace() + ); + } catch (Exception e) { + LOG_EXACTLY_ONCE.debug("{} exception: {})", e.getClass(), e.getStackTrace()); + } catch (NoClassDefFoundError e) { + LOG_EXACTLY_ONCE.debug( + "Class {} is not found error: ", + S3AG_UTIL_CLASSNAME, + e.getStackTrace() + ); } } } diff --git a/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/tools/S3AccessGrantsUtil.java b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/tools/S3AccessGrantsUtil.java new file mode 100644 index 0000000000000..27d7cbcfc522e --- /dev/null +++ b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/tools/S3AccessGrantsUtil.java @@ -0,0 +1,60 @@ +package org.apache.hadoop.fs.s3a.tools; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.s3a.DefaultS3ClientFactory; +import org.apache.hadoop.fs.store.LogExactlyOnce; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.awssdk.s3accessgrants.plugin.S3AccessGrantsPlugin; +import software.amazon.awssdk.services.s3.S3BaseClientBuilder; + +import static org.apache.hadoop.fs.s3a.Constants.AWS_S3_ACCESS_GRANTS_FALLBACK_TO_IAM_ENABLED; + +public class S3AccessGrantsUtil { + + protected static final Logger LOG = + LoggerFactory.getLogger(S3AccessGrantsUtil.class); + + private static final LogExactlyOnce LOG_EXACTLY_ONCE = new LogExactlyOnce(LOG); + private static final String S3AG_PLUGIN_CLASSNAME = + "software.amazon.awssdk.s3accessgrants.plugin.S3AccessGrantsPlugin"; + + /** + * S3 Access Grants plugin availability. + */ + private static final boolean S3AG_PLUGIN_FOUND = checkForS3AGPlugin(); + + private static boolean checkForS3AGPlugin() { + try { + ClassLoader cl = DefaultS3ClientFactory.class.getClassLoader(); + cl.loadClass(S3AG_PLUGIN_CLASSNAME); + LOG.debug("S3AG plugin class {} found", S3AG_PLUGIN_CLASSNAME); + return true; + } catch (Exception e) { + LOG.debug("S3AG plugin class {} not found", S3AG_PLUGIN_CLASSNAME, e); + return false; + } + } + + /** + * Is the S3AG plugin available? + * @return true if it was found in the classloader + */ + private static synchronized boolean isS3AGPluginAvailable() { + return S3AG_PLUGIN_FOUND; + } + + public static , ClientT> void + applyS3AccessGrantsConfigurations(BuilderT builder, Configuration conf) { + if (isS3AGPluginAvailable()) { + boolean s3agFallbackEnabled = conf.getBoolean( + AWS_S3_ACCESS_GRANTS_FALLBACK_TO_IAM_ENABLED, false); + S3AccessGrantsPlugin accessGrantsPlugin = + S3AccessGrantsPlugin.builder().enableFallback(s3agFallbackEnabled).build(); + builder.addPlugin(accessGrantsPlugin); + LOG_EXACTLY_ONCE.info("s3ag plugin is added to s3 client with fallback: {}", s3agFallbackEnabled); + } else { + LOG_EXACTLY_ONCE.warn("s3ag plugin is not available."); + } + } +} diff --git a/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/TestS3AccessGrantConfiguration.java b/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/TestS3AccessGrantConfiguration.java index 57c47d5d6e035..c74c02b1e8c30 100644 --- a/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/TestS3AccessGrantConfiguration.java +++ b/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/TestS3AccessGrantConfiguration.java @@ -19,9 +19,7 @@ package org.apache.hadoop.fs.s3a; import org.apache.hadoop.conf.Configuration; -import org.apache.hadoop.fs.s3a.impl.InstantiationIOException; import org.apache.hadoop.test.AbstractHadoopTestBase; -import org.junit.Assert; import org.junit.Test; import software.amazon.awssdk.services.s3.S3AsyncClient; @@ -79,9 +77,10 @@ private Configuration createConfig(boolean isDefault, boolean s3agEnabled) { applyVerifyS3AGPlugin(BuilderT builder, boolean isDefault, boolean enabled) { DefaultS3ClientFactory.applyS3AccessGrantsConfigurations(builder, createConfig(isDefault, enabled)); if (enabled){ - assertEquals(builder.plugins().size(), 1); - assertEquals(builder.plugins().get(0).getClass().getName(), - "software.amazon.awssdk.s3accessgrants.plugin.S3AccessGrantsPlugin"); + assertEquals(1, builder.plugins().size()); + assertEquals("software.amazon.awssdk.s3accessgrants.plugin.S3AccessGrantsPlugin", + builder.plugins().get(0).getClass().getName() + ); } else { assertEquals(builder.plugins().size(), 0); From c5c78fc31e028fcf8bb5b4ef5be458be63ac0b11 Mon Sep 17 00:00:00 2001 From: Jason Han Date: Fri, 2 Feb 2024 19:25:03 -0800 Subject: [PATCH 11/13] [S3AG] remove unnecessary change --- .../java/org/apache/hadoop/fs/s3a/DefaultS3ClientFactory.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/DefaultS3ClientFactory.java b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/DefaultS3ClientFactory.java index b201806d70be3..dd91a73b32d94 100644 --- a/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/DefaultS3ClientFactory.java +++ b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/DefaultS3ClientFactory.java @@ -418,9 +418,6 @@ private static Region getS3RegionFromEndpoint(final String endpoint, } try { Class s3agUtil = Class.forName(S3AG_UTIL_CLASSNAME); - Class[] argTypes = new Class[2]; - argTypes[0] = S3BaseClientBuilder.class; - argTypes[1] = Configuration.class; Method applyS3agConfig = s3agUtil.getMethod("applyS3AccessGrantsConfigurations", S3BaseClientBuilder.class, Configuration.class); applyS3agConfig.invoke(null, builder, conf); From 2bc7c41b6f41fe5ba3ab29e52c58ab538d1bcfa1 Mon Sep 17 00:00:00 2001 From: Jason Han Date: Mon, 5 Feb 2024 11:10:20 -0800 Subject: [PATCH 12/13] [S3AG] address comments --- hadoop-project/pom.xml | 24 ++++++++++++++ hadoop-tools/hadoop-aws/pom.xml | 19 ------------ .../hadoop/fs/s3a/DefaultS3ClientFactory.java | 13 ++++---- .../{tools => impl}/S3AccessGrantsUtil.java | 31 ++++++++++++++----- .../s3a/TestS3AccessGrantConfiguration.java | 1 + 5 files changed, 56 insertions(+), 32 deletions(-) rename hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/{tools => impl}/S3AccessGrantsUtil.java (59%) diff --git a/hadoop-project/pom.xml b/hadoop-project/pom.xml index 1a135ded88098..4c6225899079f 100644 --- a/hadoop-project/pom.xml +++ b/hadoop-project/pom.xml @@ -1976,6 +1976,30 @@ log4j-web ${log4j2.version} + + software.amazon.s3.accessgrants + aws-s3-accessgrants-java-plugin + 2.0.0 + provided + + + software.amazon.awssdk + * + + + com.github.ben-manes.caffeine + * + + + org.apache.logging.log4j + * + + + org.assertj + * + + + diff --git a/hadoop-tools/hadoop-aws/pom.xml b/hadoop-tools/hadoop-aws/pom.xml index 98ff520a5d7a5..f9e95161d0ea2 100644 --- a/hadoop-tools/hadoop-aws/pom.xml +++ b/hadoop-tools/hadoop-aws/pom.xml @@ -511,26 +511,7 @@ software.amazon.s3.accessgrants aws-s3-accessgrants-java-plugin - 2.0.0 provided - - - software.amazon.awssdk - * - - - com.github.ben-manes.caffeine - * - - - org.apache.logging.log4j - * - - - org.assertj - * - - org.assertj diff --git a/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/DefaultS3ClientFactory.java b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/DefaultS3ClientFactory.java index dd91a73b32d94..94638f0864070 100644 --- a/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/DefaultS3ClientFactory.java +++ b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/DefaultS3ClientFactory.java @@ -90,7 +90,7 @@ public class DefaultS3ClientFactory extends Configured protected static final Logger LOG = LoggerFactory.getLogger(DefaultS3ClientFactory.class); - private static final LogExactlyOnce LOG_EXACTLY_ONCE = new LogExactlyOnce(LOG); + private static final LogExactlyOnce LOG_S3AG_ENABLED = new LogExactlyOnce(LOG); /** * A one-off warning of default region chains in use. @@ -117,7 +117,7 @@ public class DefaultS3ClientFactory extends Configured "An endpoint cannot set when " + FIPS_ENDPOINT + " is true"; private static final String S3AG_UTIL_CLASSNAME = - "org.apache.hadoop.fs.s3a.tools.S3AccessGrantsUtil"; + "org.apache.hadoop.fs.s3a.impl.S3AccessGrantsUtil"; @Override public S3Client createS3Client( final URI uri, @@ -413,24 +413,25 @@ private static Region getS3RegionFromEndpoint(final String endpoint, applyS3AccessGrantsConfigurations(BuilderT builder, Configuration conf) { boolean s3agEnabled = conf.getBoolean(AWS_S3_ACCESS_GRANTS_ENABLED, false); if (!s3agEnabled){ - LOG_EXACTLY_ONCE.debug("s3ag plugin is not enabled."); + LOG.debug("S3 Access Grants plugin is not enabled."); return; } try { + LOG_S3AG_ENABLED.info("S3 Access Grants plugin is enabled."); Class s3agUtil = Class.forName(S3AG_UTIL_CLASSNAME); Method applyS3agConfig = s3agUtil.getMethod("applyS3AccessGrantsConfigurations", S3BaseClientBuilder.class, Configuration.class); applyS3agConfig.invoke(null, builder, conf); } catch (ClassNotFoundException e) { - LOG_EXACTLY_ONCE.debug( + LOG.debug( "Class {} is not found exception: {}.", S3AG_UTIL_CLASSNAME, e.getStackTrace() ); } catch (Exception e) { - LOG_EXACTLY_ONCE.debug("{} exception: {})", e.getClass(), e.getStackTrace()); + LOG.debug("{} exception: {})", e.getClass(), e.getStackTrace()); } catch (NoClassDefFoundError e) { - LOG_EXACTLY_ONCE.debug( + LOG.debug( "Class {} is not found error: ", S3AG_UTIL_CLASSNAME, e.getStackTrace() diff --git a/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/tools/S3AccessGrantsUtil.java b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/impl/S3AccessGrantsUtil.java similarity index 59% rename from hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/tools/S3AccessGrantsUtil.java rename to hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/impl/S3AccessGrantsUtil.java index 27d7cbcfc522e..923118cacf2ee 100644 --- a/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/tools/S3AccessGrantsUtil.java +++ b/hadoop-tools/hadoop-aws/src/main/java/org/apache/hadoop/fs/s3a/impl/S3AccessGrantsUtil.java @@ -1,4 +1,22 @@ -package org.apache.hadoop.fs.s3a.tools; +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.fs.s3a.impl; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.s3a.DefaultS3ClientFactory; @@ -15,7 +33,7 @@ public class S3AccessGrantsUtil { protected static final Logger LOG = LoggerFactory.getLogger(S3AccessGrantsUtil.class); - private static final LogExactlyOnce LOG_EXACTLY_ONCE = new LogExactlyOnce(LOG); + private static final LogExactlyOnce LOG_S3AG_PLUGIN_INFO = new LogExactlyOnce(LOG); private static final String S3AG_PLUGIN_CLASSNAME = "software.amazon.awssdk.s3accessgrants.plugin.S3AccessGrantsPlugin"; @@ -28,10 +46,10 @@ private static boolean checkForS3AGPlugin() { try { ClassLoader cl = DefaultS3ClientFactory.class.getClassLoader(); cl.loadClass(S3AG_PLUGIN_CLASSNAME); - LOG.debug("S3AG plugin class {} found", S3AG_PLUGIN_CLASSNAME); + LOG.debug("S3 Access Grants plugin class {} found", S3AG_PLUGIN_CLASSNAME); return true; } catch (Exception e) { - LOG.debug("S3AG plugin class {} not found", S3AG_PLUGIN_CLASSNAME, e); + LOG.debug("S3 Access Grants plugin class {} not found", S3AG_PLUGIN_CLASSNAME, e); return false; } } @@ -52,9 +70,8 @@ private static synchronized boolean isS3AGPluginAvailable() { S3AccessGrantsPlugin accessGrantsPlugin = S3AccessGrantsPlugin.builder().enableFallback(s3agFallbackEnabled).build(); builder.addPlugin(accessGrantsPlugin); - LOG_EXACTLY_ONCE.info("s3ag plugin is added to s3 client with fallback: {}", s3agFallbackEnabled); - } else { - LOG_EXACTLY_ONCE.warn("s3ag plugin is not available."); + LOG_S3AG_PLUGIN_INFO.info("S3 Access Grants plugin is added to s3 client with fallback: {}", s3agFallbackEnabled); } } + } diff --git a/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/TestS3AccessGrantConfiguration.java b/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/TestS3AccessGrantConfiguration.java index c74c02b1e8c30..3846bf9998fc7 100644 --- a/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/TestS3AccessGrantConfiguration.java +++ b/hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/TestS3AccessGrantConfiguration.java @@ -86,4 +86,5 @@ private Configuration createConfig(boolean isDefault, boolean s3agEnabled) { assertEquals(builder.plugins().size(), 0); } } + } \ No newline at end of file From dcbab4bc5973ef4886ebe3c350160fcb7f9df0f6 Mon Sep 17 00:00:00 2001 From: Jason Han Date: Mon, 5 Feb 2024 14:11:55 -0800 Subject: [PATCH 13/13] [S3AG] add documentation for S3AG --- .../tools/hadoop-aws/s3_access_grants.md | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 hadoop-tools/hadoop-aws/src/site/markdown/tools/hadoop-aws/s3_access_grants.md diff --git a/hadoop-tools/hadoop-aws/src/site/markdown/tools/hadoop-aws/s3_access_grants.md b/hadoop-tools/hadoop-aws/src/site/markdown/tools/hadoop-aws/s3_access_grants.md new file mode 100644 index 0000000000000..5af1ad654517b --- /dev/null +++ b/hadoop-tools/hadoop-aws/src/site/markdown/tools/hadoop-aws/s3_access_grants.md @@ -0,0 +1,61 @@ + + +# S3 Access Grants + + + +S3 Access Grants is a credential vending service for S3 data. More information: +* https://aws.amazon.com/s3/features/access-grants/ + +In S3A, S3 Access Grants Plugin is used to support S3 Access Grants. More information: +* https://github.com/aws/aws-s3-accessgrants-plugin-java-v2/ + + + +## How to enable S3 Access Grants in S3A + +1. Add the `hadoop-aws` JAR on your classpath. + +1. Add the `aws-java-sdk-bundle.jar` JAR to your classpath, the minimum version is v2.23.7. + +2. Add the `aws-s3-accessgrants-java-plugin-2.0.0.jar` JAR to your classpath. +3. Add the `caffeine.jar` JAR to your classpath. + +1. Add configurations to enable S3 Access Grants in `core-site.xml` + + + +Example: + +```xml + +... + + fs.s3a.accessgrants.enabled + true + Enable S3 Access Grants or not + + + fs.s3a.accessgrants.fallbacktoiam + false + Enable IAM Policy as fallback or not + +... + +``` + + + +