Skip to content

AWS: update test cases to verify credentials for the prefixed S3 client#13118

Merged
nastra merged 9 commits into
apache:mainfrom
plusplusjiajia:fix-s3fileio
Jun 2, 2025
Merged

AWS: update test cases to verify credentials for the prefixed S3 client#13118
nastra merged 9 commits into
apache:mainfrom
plusplusjiajia:fix-s3fileio

Conversation

@plusplusjiajia

Copy link
Copy Markdown
Member

Pass the storage credential when initializing S3FileIOAwsClientFactories.

@github-actions github-actions Bot added the AWS label May 21, 2025

@lliangyu-lin lliangyu-lin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

assertThat(actualConfiguration).isNotNull();
try {
actualConfiguration.credentialsProvider().resolveIdentity();
} catch (SdkClientException e) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please use assertThatThrownBy(..).isInstanceOf(..).hasMessage(..) instead of a try-catch block

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please use assertThatThrownBy(..).isInstanceOf(..).hasMessage(..) instead of a try-catch block

thanks for your advice, updated.

// Do not override s3 client if it was provided
if (s3 == null) {
Object clientFactory = S3FileIOAwsClientFactories.initialize(props);
Object clientFactory = S3FileIOAwsClientFactories.initialize(propertiesWithCredentials);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI this is going to be fixed by #12799 but it's still good to get the tests in

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI this is going to be fixed by #12799 but it's still good to get the tests in
Thanks for the reminder, this feature looks great!

@nastra nastra closed this May 22, 2025
@nastra nastra reopened this May 22, 2025
@nastra

nastra commented May 22, 2025

Copy link
Copy Markdown
Contributor

#12799 has been merged before I had the chance to merge your PR. Can you rebase this please so that we can get the tests in

@plusplusjiajia

Copy link
Copy Markdown
Member Author

#12799 has been merged before I had the chance to merge your PR. Can you rebase this please so that we can get the tests in

sure, I've reverted the change.

@nastra nastra left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we pretty much have already existing tests where the storage credentials (and the credentials in the properties) are configured. So rather than introducing new test methods I would suggest to just add the important assertion to the existing tests. This would then look like the following diff and should cover all cases that are needed.

Also please make sure to update the PR title to reflect the scope/intention (since it's not passing storage credentials anymore)

diff --git a/aws/src/integration/java/org/apache/iceberg/aws/s3/TestS3FileIO.java b/aws/src/integration/java/org/apache/iceberg/aws/s3/TestS3FileIO.java
index 85453cba97..965e1c6068 100644
--- a/aws/src/integration/java/org/apache/iceberg/aws/s3/TestS3FileIO.java
+++ b/aws/src/integration/java/org/apache/iceberg/aws/s3/TestS3FileIO.java
@@ -41,6 +41,7 @@ import java.util.Random;
 import java.util.Spliterator;
 import java.util.Spliterators;
 import java.util.UUID;
+import java.util.concurrent.ExecutionException;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
@@ -89,8 +90,12 @@ import org.mockito.Mockito;
 import org.testcontainers.containers.MinIOContainer;
 import org.testcontainers.junit.jupiter.Container;
 import org.testcontainers.junit.jupiter.Testcontainers;
+import software.amazon.awssdk.awscore.AwsServiceClientConfiguration;
+import software.amazon.awssdk.core.exception.SdkClientException;
 import software.amazon.awssdk.core.sync.RequestBody;
 import software.amazon.awssdk.http.urlconnection.UrlConnectionHttpClient;
+import software.amazon.awssdk.identity.spi.AwsSessionCredentialsIdentity;
+import software.amazon.awssdk.identity.spi.IdentityProvider;
 import software.amazon.awssdk.regions.Region;
 import software.amazon.awssdk.services.s3.S3AsyncClient;
 import software.amazon.awssdk.services.s3.S3Client;
@@ -730,10 +735,23 @@ public class TestS3FileIO {
     s3FileIOProperties
         .extracting(S3FileIOProperties::sessionToken)
         .isEqualTo("sessionTokenFromProperties");
+
+    // verify that the credentials identity gets the correct credentials for the prefixed S3 client
+    assertThat(fileIO.client("s3://my-bucket/table1").serviceClientConfiguration())
+        .extracting(AwsServiceClientConfiguration::credentialsProvider)
+        .extracting(IdentityProvider::resolveIdentity)
+        .satisfies(
+            x -> {
+              AwsSessionCredentialsIdentity identity = (AwsSessionCredentialsIdentity) x.get();
+              assertThat(identity).isNotNull();
+              assertThat(identity.accessKeyId()).isEqualTo("keyIdFromProperties");
+              assertThat(identity.secretAccessKey()).isEqualTo("accessKeyFromProperties");
+              assertThat(identity.sessionToken()).isEqualTo("sessionTokenFromProperties");
+            });
   }
 
   @Test
-  public void singleStorageCredentialConfigured() {
+  public void singleStorageCredentialConfigured() throws ExecutionException, InterruptedException {
     StorageCredential s3Credential =
         StorageCredential.create(
             "s3://custom-uri",
@@ -782,6 +800,32 @@ public class TestS3FileIO {
     s3FileIOProperties
         .extracting(S3FileIOProperties::sessionToken)
         .isEqualTo("sessionTokenFromCredential");
+
+    // verify that the credentials identity gets the correct credentials for the generic S3 client
+    assertThat(fileIO.client().serviceClientConfiguration())
+        .extracting(AwsServiceClientConfiguration::credentialsProvider)
+        .extracting(IdentityProvider::resolveIdentity)
+        .satisfies(
+            x -> {
+              AwsSessionCredentialsIdentity identity = (AwsSessionCredentialsIdentity) x.get();
+              assertThat(identity).isNotNull();
+              assertThat(identity.accessKeyId()).isEqualTo("keyIdFromProperties");
+              assertThat(identity.secretAccessKey()).isEqualTo("accessKeyFromProperties");
+              assertThat(identity.sessionToken()).isEqualTo("sessionTokenFromProperties");
+            });
+
+    // verify that the credentials identity gets the correct credentials for the prefixed S3 client
+    assertThat(fileIO.client("s3://custom-uri/table2").serviceClientConfiguration())
+        .extracting(AwsServiceClientConfiguration::credentialsProvider)
+        .extracting(IdentityProvider::resolveIdentity)
+        .satisfies(
+            x -> {
+              AwsSessionCredentialsIdentity identity = (AwsSessionCredentialsIdentity) x.get();
+              assertThat(identity).isNotNull();
+              assertThat(identity.accessKeyId()).isEqualTo("keyIdFromCredential");
+              assertThat(identity.secretAccessKey()).isEqualTo("accessKeyFromCredential");
+              assertThat(identity.sessionToken()).isEqualTo("sessionTokenFromCredential");
+            });
   }
 
   @Test
@@ -851,6 +895,19 @@ public class TestS3FileIO {
         .extracting(S3FileIOProperties::sessionToken)
         .isEqualTo("sessionTokenFromCredential1");
 
+    // verify that the credentials identity gets the correct credentials for the prefixed S3 client
+    assertThat(fileIO.client("s3://custom-uri/1").serviceClientConfiguration())
+        .extracting(AwsServiceClientConfiguration::credentialsProvider)
+        .extracting(IdentityProvider::resolveIdentity)
+        .satisfies(
+            x -> {
+              AwsSessionCredentialsIdentity identity = (AwsSessionCredentialsIdentity) x.get();
+              assertThat(identity).isNotNull();
+              assertThat(identity.accessKeyId()).isEqualTo("keyIdFromCredential1");
+              assertThat(identity.secretAccessKey()).isEqualTo("accessKeyFromCredential1");
+              assertThat(identity.sessionToken()).isEqualTo("sessionTokenFromCredential1");
+            });
+
     ObjectAssert<S3FileIOProperties> s3FileIOProperties2 =
         assertThat(fileIO.clientForStoragePath("s3://custom-uri/2/table1").s3FileIOProperties());
     s3FileIOProperties2
@@ -862,6 +919,62 @@ public class TestS3FileIO {
     s3FileIOProperties2
         .extracting(S3FileIOProperties::sessionToken)
         .isEqualTo("sessionTokenFromCredential2");
+
+    // verify that the credentials identity gets the correct credentials for the prefixed S3 client
+    assertThat(fileIO.client("s3://custom-uri/2").serviceClientConfiguration())
+        .extracting(AwsServiceClientConfiguration::credentialsProvider)
+        .extracting(IdentityProvider::resolveIdentity)
+        .satisfies(
+            x -> {
+              AwsSessionCredentialsIdentity identity = (AwsSessionCredentialsIdentity) x.get();
+              assertThat(identity).isNotNull();
+              assertThat(identity.accessKeyId()).isEqualTo("keyIdFromCredential2");
+              assertThat(identity.secretAccessKey()).isEqualTo("accessKeyFromCredential2");
+              assertThat(identity.sessionToken()).isEqualTo("sessionTokenFromCredential2");
+            });
+  }
+
+  @Test
+  public void storageCredentialsConfiguredWithoutCredentialsInProperties() {
+    StorageCredential s3Credential =
+        StorageCredential.create(
+            "s3://custom-uri/1",
+            ImmutableMap.of(
+                "s3.access-key-id",
+                "keyIdFromCredential",
+                "s3.secret-access-key",
+                "accessKeyFromCredential",
+                "s3.session-token",
+                "sessionTokenFromCredential"));
+
+    S3FileIO fileIO = new S3FileIO();
+    fileIO.setCredentials(ImmutableList.of(s3Credential));
+    fileIO.initialize(ImmutableMap.of("client.region", "us-east-1"));
+
+    // verify that the credentials identity gets the correct credentials for the prefixed S3 client
+    assertThat(fileIO.client("s3://custom-uri/1").serviceClientConfiguration())
+        .extracting(AwsServiceClientConfiguration::credentialsProvider)
+        .extracting(IdentityProvider::resolveIdentity)
+        .satisfies(
+            x -> {
+              AwsSessionCredentialsIdentity identity = (AwsSessionCredentialsIdentity) x.get();
+              assertThat(identity).isNotNull();
+              assertThat(identity.accessKeyId()).isEqualTo("keyIdFromCredential");
+              assertThat(identity.secretAccessKey()).isEqualTo("accessKeyFromCredential");
+              assertThat(identity.sessionToken()).isEqualTo("sessionTokenFromCredential");
+            });
+
+    // there are no credentials for the generic S3 client configured, since there are no credentials
+    // passed in the properties
+    assertThatThrownBy(
+            () ->
+                fileIO
+                    .client()
+                    .serviceClientConfiguration()
+                    .credentialsProvider()
+                    .resolveIdentity())
+        .isInstanceOf(SdkClientException.class)
+        .hasMessageContaining("Unable to load credentials from any of the providers");
   }
 
   private void createRandomObjects(String prefix, int count) {

@plusplusjiajia plusplusjiajia changed the title AWS: pass storage credentials to s3 client AWS: update test cases to verify credentials for the prefixed S3 client May 24, 2025
}

@Test
public void storageCredentialsConfiguredOverwriteCredentialsInProperties()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is already covered in existing tests

}

@Test
public void storageCredentialsConfiguredWithoutCredentialsInProperties() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is already covered in existing tests

@nastra nastra left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM but I would remove the two test methods that I commented on, since those are already covered in existing tests

@nastra nastra merged commit 4218554 into apache:main Jun 2, 2025
42 checks passed
talatuyarer pushed a commit to talatuyarer/iceberg that referenced this pull request Jun 2, 2025
devendra-nr pushed a commit to devendra-nr/iceberg that referenced this pull request Dec 8, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants