You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@polaris.apache.org by di...@apache.org on 2026/03/23 14:02:43 UTC
(polaris) branch main updated: Add MinIO coverage for AWS-shaped KMS vended credentials (#4034)
This is an automated email from the ASF dual-hosted git repository.
dimas pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/polaris.git
The following commit(s) were added to refs/heads/main by this push:
new fd7adbcafc Add MinIO coverage for AWS-shaped KMS vended credentials (#4034)
fd7adbcafc is described below
commit fd7adbcafc17f62cf1e1ac0d5d38546b5ad5c888
Author: Francisco Castanheira <32...@users.noreply.github.com>
AuthorDate: Mon Mar 23 14:02:39 2026 +0000
Add MinIO coverage for AWS-shaped KMS vended credentials (#4034)
* Add MinIO KMS edge case integration tests
* Refactor MinIO KMS edge case tests
---
.../service/it/RestCatalogMinIOSpecialIT.java | 176 +++++++++++++++++++--
1 file changed, 161 insertions(+), 15 deletions(-)
diff --git a/runtime/service/src/intTest/java/org/apache/polaris/service/it/RestCatalogMinIOSpecialIT.java b/runtime/service/src/intTest/java/org/apache/polaris/service/it/RestCatalogMinIOSpecialIT.java
index 3cc2ac9648..7fe3df75ea 100644
--- a/runtime/service/src/intTest/java/org/apache/polaris/service/it/RestCatalogMinIOSpecialIT.java
+++ b/runtime/service/src/intTest/java/org/apache/polaris/service/it/RestCatalogMinIOSpecialIT.java
@@ -103,6 +103,8 @@ public class RestCatalogMinIOSpecialIT {
private static final String BUCKET_URI_PREFIX = "/minio-test";
private static final String MINIO_ACCESS_KEY = "test-ak-123";
private static final String MINIO_SECRET_KEY = "test-sk-123";
+ private static final String TEST_REGION = "us-west-2";
+ private static final String TEST_ROLE_ARN = "arn:aws:iam::000000000000:role/polaris-access-role";
private static String adminToken;
public static class Profile implements QuarkusTestProfile {
@@ -172,7 +174,15 @@ public class RestCatalogMinIOSpecialIT {
Optional<AccessDelegationMode> delegationMode,
boolean stsEnabled) {
return createCatalog(
- endpoint, stsEndpoint, pathStyleAccess, Optional.empty(), delegationMode, stsEnabled);
+ endpoint,
+ stsEndpoint,
+ pathStyleAccess,
+ Optional.empty(),
+ delegationMode,
+ stsEnabled,
+ Optional.empty(),
+ Optional.empty(),
+ Optional.empty());
}
private RESTCatalog createCatalog(
@@ -182,6 +192,28 @@ public class RestCatalogMinIOSpecialIT {
Optional<String> endpointInternal,
Optional<AccessDelegationMode> delegationMode,
boolean stsEnabled) {
+ return createCatalog(
+ endpoint,
+ stsEndpoint,
+ pathStyleAccess,
+ endpointInternal,
+ delegationMode,
+ stsEnabled,
+ Optional.empty(),
+ Optional.empty(),
+ Optional.empty());
+ }
+
+ private RESTCatalog createCatalog(
+ Optional<String> endpoint,
+ Optional<String> stsEndpoint,
+ boolean pathStyleAccess,
+ Optional<String> endpointInternal,
+ Optional<AccessDelegationMode> delegationMode,
+ boolean stsEnabled,
+ Optional<String> region,
+ Optional<String> roleArn,
+ Optional<Boolean> kmsUnavailable) {
AwsStorageConfigInfo.Builder storageConfig =
AwsStorageConfigInfo.builder()
.setStorageType(StorageConfigInfo.StorageTypeEnum.S3)
@@ -192,6 +224,9 @@ public class RestCatalogMinIOSpecialIT {
endpoint.ifPresent(storageConfig::setEndpoint);
stsEndpoint.ifPresent(storageConfig::setStsEndpoint);
endpointInternal.ifPresent(storageConfig::setEndpointInternal);
+ region.ifPresent(storageConfig::setRegion);
+ roleArn.ifPresent(storageConfig::setRoleArn);
+ kmsUnavailable.ifPresent(storageConfig::setKmsUnavailable);
CatalogProperties.Builder catalogProps =
CatalogProperties.builder(storageBase.toASCIIString() + "/" + catalogName);
@@ -265,6 +300,107 @@ public class RestCatalogMinIOSpecialIT {
assertThat(response.credentials()).hasSize(1);
}
+ @ParameterizedTest
+ @CsvSource({
+ "false, false",
+ "true, false",
+ "false, true",
+ })
+ public void testCreateTableVendedCredentialsWithPartialAwsShapePasses(
+ boolean includeRegion, boolean includeRoleArn) throws IOException {
+ try (RESTCatalog restCatalog =
+ createCatalog(
+ Optional.of(endpoint),
+ Optional.of(endpoint),
+ true,
+ Optional.empty(),
+ Optional.of(VENDED_CREDENTIALS),
+ true,
+ includeRegion ? Optional.of(TEST_REGION) : Optional.empty(),
+ includeRoleArn ? Optional.of(TEST_ROLE_ARN) : Optional.empty(),
+ Optional.of(false))) {
+ TableIdentifier id = createTableAndVerifyMetadata(restCatalog);
+ try {
+ assertLoadTableWithVendedCredentialsSucceeds(id);
+ } finally {
+ catalogApi.dropTable(catalogName, id);
+ }
+ }
+ }
+
+ @Test
+ public void testCreateTableVendedCredentialsWithFullAwsShapeAndKmsEnabledFails()
+ throws IOException {
+ try (RESTCatalog restCatalog =
+ createCatalog(
+ Optional.of(endpoint),
+ Optional.of(endpoint),
+ true,
+ Optional.empty(),
+ Optional.of(VENDED_CREDENTIALS),
+ true,
+ Optional.of(TEST_REGION),
+ Optional.of(TEST_ROLE_ARN),
+ Optional.of(false))) {
+ TableIdentifier id = createTableAndVerifyMetadata(restCatalog);
+ try {
+ assertLoadTableWithVendedCredentialsFailsWithKmsError(id);
+ } finally {
+ catalogApi.dropTable(catalogName, id);
+ }
+ }
+ }
+
+ @Test
+ public void testCreateTableVendedCredentialsWithAwsShapeAndKmsUnavailablePasses()
+ throws IOException {
+ try (RESTCatalog restCatalog =
+ createCatalog(
+ Optional.of(endpoint),
+ Optional.of(endpoint),
+ true,
+ Optional.empty(),
+ Optional.of(VENDED_CREDENTIALS),
+ true,
+ Optional.of(TEST_REGION),
+ Optional.of(TEST_ROLE_ARN),
+ Optional.of(true))) {
+ TableIdentifier id = createTableAndVerifyMetadata(restCatalog);
+ try {
+ assertLoadTableWithVendedCredentialsSucceeds(id);
+ } finally {
+ catalogApi.dropTable(catalogName, id);
+ }
+ }
+ }
+
+ private void assertLoadTableWithVendedCredentialsFailsWithKmsError(TableIdentifier id) {
+ assertThatThrownBy(
+ () ->
+ catalogApi.loadTable(
+ catalogName,
+ id,
+ "ALL",
+ Map.of("X-Iceberg-Access-Delegation", VENDED_CREDENTIALS.protocolValue())))
+ .hasMessageContaining("Failed to get subscoped credentials")
+ .hasMessageContaining("invalid resource")
+ .hasMessageContaining("arn:aws:kms:us-west-2:000000000000:key/*");
+ }
+
+ private void assertLoadTableWithVendedCredentialsSucceeds(TableIdentifier id) {
+ LoadTableResponse response =
+ catalogApi.loadTable(
+ catalogName,
+ id,
+ "ALL",
+ Map.of("X-Iceberg-Access-Delegation", VENDED_CREDENTIALS.protocolValue()));
+ assertThat(response.config())
+ .containsEntry(
+ REFRESH_CREDENTIALS_ENDPOINT,
+ "v1/" + catalogName + "/namespaces/test-ns/tables/t1/credentials");
+ assertThat(response.credentials()).hasSize(1);
+ }
+
private LoadTableResponse doTestCreateTable(
boolean pathStyle, Optional<AccessDelegationMode> dm, boolean stsEnabled) throws IOException {
try (RESTCatalog restCatalog =
@@ -360,11 +496,33 @@ public class RestCatalogMinIOSpecialIT {
public LoadTableResponse doTestCreateTable(
RESTCatalog restCatalog, Optional<AccessDelegationMode> dm) {
+ TableIdentifier id = createTableAndVerifyMetadata(restCatalog);
+ assertThat(restCatalog.tableExists(id)).isTrue();
+
+ try {
+ LoadTableResponse loadTableResponse =
+ catalogApi.loadTable(
+ catalogName,
+ id,
+ "ALL",
+ dm.map(v -> Map.of("X-Iceberg-Access-Delegation", v.protocolValue()))
+ .orElse(Map.of()));
+
+ assertThat(loadTableResponse.config()).containsKey(ENDPOINT);
+ return loadTableResponse;
+ } finally {
+ if (restCatalog.tableExists(id)) {
+ restCatalog.dropTable(id);
+ assertThat(restCatalog.tableExists(id)).isFalse();
+ }
+ }
+ }
+
+ private TableIdentifier createTableAndVerifyMetadata(RESTCatalog restCatalog) {
catalogApi.createNamespace(catalogName, "test-ns");
TableIdentifier id = TableIdentifier.of("test-ns", "t1");
Table table = restCatalog.createTable(id, SCHEMA);
assertThat(table).isNotNull();
- assertThat(restCatalog.tableExists(id)).isTrue();
TableOperations ops = ((HasTableOperations) table).operations();
URI location = URI.create(ops.current().metadataFileLocation());
@@ -378,19 +536,7 @@ public class RestCatalogMinIOSpecialIT {
.build())
.response();
assertThat(response.contentLength()).isGreaterThan(0);
-
- LoadTableResponse loadTableResponse =
- catalogApi.loadTable(
- catalogName,
- id,
- "ALL",
- dm.map(v -> Map.of("X-Iceberg-Access-Delegation", v.protocolValue())).orElse(Map.of()));
-
- assertThat(loadTableResponse.config()).containsKey(ENDPOINT);
-
- restCatalog.dropTable(id);
- assertThat(restCatalog.tableExists(id)).isFalse();
- return loadTableResponse;
+ return id;
}
@ParameterizedTest