This is an automated email from the ASF dual-hosted git repository. snazy 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 355adae1f Make `*StorageConfigurationInfo` types immutable (#2236) 355adae1f is described below commit 355adae1f6169b91bc78b0fe71ff863ee19798e8 Author: Robert Stupp <sn...@snazy.de> AuthorDate: Tue Aug 5 13:47:40 2025 +0200 Make `*StorageConfigurationInfo` types immutable (#2236) This change eventually enables usage of the `*StorageConfigurationInfo` in the `StorageCredentialCacheKey` due to the then memoized hash-code values, to eliminate a couple of JSON re-serializations. --- .../apache/polaris/core/entity/CatalogEntity.java | 48 +++--- .../core/storage/FileStorageConfigurationInfo.java | 24 ++- .../storage/PolarisStorageConfigurationInfo.java | 45 +++--- .../core/storage/StorageConfigurationOverride.java | 18 ++- .../storage/aws/AwsStorageConfigurationInfo.java | 165 +++++---------------- .../azure/AzureStorageConfigurationInfo.java | 72 +++------ .../storage/gcp/GcpStorageConfigurationInfo.java | 48 +++--- .../storage/InMemoryStorageIntegrationTest.java | 44 +++--- .../aws/AwsStorageConfigurationInfoTest.java | 64 ++++---- .../PolarisStorageConfigurationInfoTest.java | 118 +++++++++++++++ .../aws/AwsCredentialsStorageIntegrationTest.java | 112 +++++++------- .../AzureCredentialStorageIntegrationTest.java | 9 +- .../gcp/GcpCredentialsStorageIntegrationTest.java | 10 +- 13 files changed, 395 insertions(+), 382 deletions(-) diff --git a/polaris-core/src/main/java/org/apache/polaris/core/entity/CatalogEntity.java b/polaris-core/src/main/java/org/apache/polaris/core/entity/CatalogEntity.java index ba8627f06..622df1fca 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/entity/CatalogEntity.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/entity/CatalogEntity.java @@ -22,7 +22,6 @@ import static org.apache.polaris.core.admin.model.StorageConfigInfo.StorageTypeE import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; -import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; @@ -269,37 +268,40 @@ public class CatalogEntity extends PolarisEntity implements LocationBasedEntity case S3: AwsStorageConfigInfo awsConfigModel = (AwsStorageConfigInfo) storageConfigModel; AwsStorageConfigurationInfo awsConfig = - new AwsStorageConfigurationInfo( - PolarisStorageConfigurationInfo.StorageType.S3, - new ArrayList<>(allowedLocations), - awsConfigModel.getRoleArn(), - awsConfigModel.getExternalId(), - awsConfigModel.getRegion(), - awsConfigModel.getEndpoint(), - awsConfigModel.getStsEndpoint(), - awsConfigModel.getPathStyleAccess(), - awsConfigModel.getEndpointInternal()); + AwsStorageConfigurationInfo.builder() + .allowedLocations(allowedLocations) + .roleARN(awsConfigModel.getRoleArn()) + .externalId(awsConfigModel.getExternalId()) + .region(awsConfigModel.getRegion()) + .endpoint(awsConfigModel.getEndpoint()) + .stsEndpoint(awsConfigModel.getStsEndpoint()) + .pathStyleAccess(awsConfigModel.getPathStyleAccess()) + .endpointInternal(awsConfigModel.getEndpointInternal()) + .build(); awsConfig.validateArn(awsConfigModel.getRoleArn()); config = awsConfig; break; case AZURE: AzureStorageConfigInfo azureConfigModel = (AzureStorageConfigInfo) storageConfigModel; - AzureStorageConfigurationInfo azureConfigInfo = - new AzureStorageConfigurationInfo( - new ArrayList<>(allowedLocations), azureConfigModel.getTenantId()); - azureConfigInfo.setMultiTenantAppName(azureConfigModel.getMultiTenantAppName()); - azureConfigInfo.setConsentUrl(azureConfigModel.getConsentUrl()); - config = azureConfigInfo; + config = + AzureStorageConfigurationInfo.builder() + .allowedLocations(allowedLocations) + .tenantId(azureConfigModel.getTenantId()) + .multiTenantAppName(azureConfigModel.getMultiTenantAppName()) + .consentUrl(azureConfigModel.getConsentUrl()) + .build(); break; case GCS: - GcpStorageConfigurationInfo gcpConfig = - new GcpStorageConfigurationInfo(new ArrayList<>(allowedLocations)); - gcpConfig.setGcpServiceAccount( - ((GcpStorageConfigInfo) storageConfigModel).getGcsServiceAccount()); - config = gcpConfig; + config = + GcpStorageConfigurationInfo.builder() + .allowedLocations(allowedLocations) + .gcpServiceAccount( + ((GcpStorageConfigInfo) storageConfigModel).getGcsServiceAccount()) + .build(); break; case FILE: - config = new FileStorageConfigurationInfo(new ArrayList<>(allowedLocations)); + config = + FileStorageConfigurationInfo.builder().allowedLocations(allowedLocations).build(); break; default: throw new IllegalStateException( diff --git a/polaris-core/src/main/java/org/apache/polaris/core/storage/FileStorageConfigurationInfo.java b/polaris-core/src/main/java/org/apache/polaris/core/storage/FileStorageConfigurationInfo.java index 6b1d48c5c..6b4fe51f7 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/storage/FileStorageConfigurationInfo.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/storage/FileStorageConfigurationInfo.java @@ -18,22 +18,25 @@ */ package org.apache.polaris.core.storage; -import com.fasterxml.jackson.annotation.JsonProperty; -import jakarta.annotation.Nonnull; -import java.util.List; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; import java.util.Locale; +import org.apache.polaris.immutables.PolarisImmutable; /** * Support for file:// URLs in storage configuration. This is pretty-much only used for testing. * Supports URLs that start with file:// or /, but also supports wildcard (*) to support certain * test cases. */ -public class FileStorageConfigurationInfo extends PolarisStorageConfigurationInfo { +@PolarisImmutable +@JsonSerialize(as = ImmutableFileStorageConfigurationInfo.class) +@JsonDeserialize(as = ImmutableFileStorageConfigurationInfo.class) +@JsonTypeName("FileStorageConfigurationInfo") +public abstract class FileStorageConfigurationInfo extends PolarisStorageConfigurationInfo { - public FileStorageConfigurationInfo( - @JsonProperty(value = "allowedLocations", required = true) @Nonnull - List<String> allowedLocations) { - super(StorageType.FILE, allowedLocations); + public static ImmutableFileStorageConfigurationInfo.Builder builder() { + return ImmutableFileStorageConfigurationInfo.builder(); } @Override @@ -41,6 +44,11 @@ public class FileStorageConfigurationInfo extends PolarisStorageConfigurationInf return "org.apache.iceberg.hadoop.HadoopFileIO"; } + @Override + public StorageType getStorageType() { + return StorageType.FILE; + } + @Override public void validatePrefixForStorageType(String loc) { if (getStorageType().getPrefixes().stream() diff --git a/polaris-core/src/main/java/org/apache/polaris/core/storage/PolarisStorageConfigurationInfo.java b/polaris-core/src/main/java/org/apache/polaris/core/storage/PolarisStorageConfigurationInfo.java index d039b1f61..414156fb4 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/storage/PolarisStorageConfigurationInfo.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/storage/PolarisStorageConfigurationInfo.java @@ -18,8 +18,9 @@ */ package org.apache.polaris.core.storage; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.core.JsonProcessingException; @@ -46,6 +47,7 @@ import org.apache.polaris.core.entity.table.IcebergTableLikeEntity; import org.apache.polaris.core.storage.aws.AwsStorageConfigurationInfo; import org.apache.polaris.core.storage.azure.AzureStorageConfigurationInfo; import org.apache.polaris.core.storage.gcp.GcpStorageConfigurationInfo; +import org.immutables.value.Value; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -65,40 +67,28 @@ import org.slf4j.LoggerFactory; @JsonSubTypes.Type(value = GcpStorageConfigurationInfo.class), @JsonSubTypes.Type(value = FileStorageConfigurationInfo.class), }) +@JsonIgnoreProperties(ignoreUnknown = true) public abstract class PolarisStorageConfigurationInfo { private static final Logger LOGGER = LoggerFactory.getLogger(PolarisStorageConfigurationInfo.class); - // a list of allowed locations - private final List<String> allowedLocations; - - // storage type - private final StorageType storageType; - - public PolarisStorageConfigurationInfo( - @JsonProperty(value = "storageType", required = true) @Nonnull StorageType storageType, - @JsonProperty(value = "allowedLocations", required = true) @Nonnull - List<String> allowedLocations) { - this(storageType, allowedLocations, true); - } - - protected PolarisStorageConfigurationInfo( - StorageType storageType, List<String> allowedLocations, boolean validatePrefix) { - this.allowedLocations = allowedLocations; - this.storageType = storageType; - if (validatePrefix) { - allowedLocations.forEach(this::validatePrefixForStorageType); + @Value.Check + protected void check() { + if (validatePrefix()) { + getAllowedLocations().forEach(this::validatePrefixForStorageType); } } - public List<String> getAllowedLocations() { - return allowedLocations; + @JsonIgnore + @Value.Auxiliary + public boolean validatePrefix() { + return true; } - public StorageType getStorageType() { - return storageType; - } + public abstract List<String> getAllowedLocations(); + + public abstract StorageType getStorageType(); private static final ObjectMapper DEFAULT_MAPPER; @@ -213,11 +203,12 @@ public abstract class PolarisStorageConfigurationInfo { /** Validate if the provided allowed locations are valid for the storage type */ protected void validatePrefixForStorageType(String loc) { - if (storageType.prefixes.stream().noneMatch(p -> loc.toLowerCase(Locale.ROOT).startsWith(p))) { + if (getStorageType().prefixes.stream() + .noneMatch(p -> loc.toLowerCase(Locale.ROOT).startsWith(p))) { throw new IllegalArgumentException( String.format( "Location prefix not allowed: '%s', expected prefixes: '%s'", - loc, String.join(",", storageType.prefixes))); + loc, String.join(",", getStorageType().prefixes))); } } diff --git a/polaris-core/src/main/java/org/apache/polaris/core/storage/StorageConfigurationOverride.java b/polaris-core/src/main/java/org/apache/polaris/core/storage/StorageConfigurationOverride.java index 1d695dbb1..b38ebf2fc 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/storage/StorageConfigurationOverride.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/storage/StorageConfigurationOverride.java @@ -30,20 +30,36 @@ import java.util.List; public class StorageConfigurationOverride extends PolarisStorageConfigurationInfo { private final PolarisStorageConfigurationInfo parentStorageConfiguration; + private final List<String> allowedLocations; public StorageConfigurationOverride( @Nonnull PolarisStorageConfigurationInfo parentStorageConfiguration, List<String> allowedLocations) { - super(parentStorageConfiguration.getStorageType(), allowedLocations, false); this.parentStorageConfiguration = parentStorageConfiguration; + this.allowedLocations = List.copyOf(allowedLocations); allowedLocations.forEach(this::validatePrefixForStorageType); } + @Override + public List<String> getAllowedLocations() { + return allowedLocations; + } + + @Override + public StorageType getStorageType() { + return parentStorageConfiguration.getStorageType(); + } + @Override public String getFileIoImplClassName() { return parentStorageConfiguration.getFileIoImplClassName(); } + @Override + public boolean validatePrefix() { + return false; + } + // delegate to the wrapped class in case they override the parent behavior @Override protected void validatePrefixForStorageType(String loc) { diff --git a/polaris-core/src/main/java/org/apache/polaris/core/storage/aws/AwsStorageConfigurationInfo.java b/polaris-core/src/main/java/org/apache/polaris/core/storage/aws/AwsStorageConfigurationInfo.java index bd325d103..4181d205e 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/storage/aws/AwsStorageConfigurationInfo.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/storage/aws/AwsStorageConfigurationInfo.java @@ -18,20 +18,27 @@ */ package org.apache.polaris.core.storage.aws; -import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.base.MoreObjects; -import jakarta.annotation.Nonnull; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; import jakarta.annotation.Nullable; import java.net.URI; -import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.polaris.core.storage.PolarisStorageConfigurationInfo; +import org.apache.polaris.immutables.PolarisImmutable; /** Aws Polaris Storage Configuration information */ -public class AwsStorageConfigurationInfo extends PolarisStorageConfigurationInfo { +@PolarisImmutable +@JsonSerialize(as = ImmutableAwsStorageConfigurationInfo.class) +@JsonDeserialize(as = ImmutableAwsStorageConfigurationInfo.class) +@JsonTypeName("AwsStorageConfigurationInfo") +public abstract class AwsStorageConfigurationInfo extends PolarisStorageConfigurationInfo { + + public static ImmutableAwsStorageConfigurationInfo.Builder builder() { + return ImmutableAwsStorageConfigurationInfo.builder(); + } // Technically, it should be ^arn:(aws|aws-cn|aws-us-gov):iam::(\d{12}):role/.+$, @JsonIgnore @@ -39,74 +46,9 @@ public class AwsStorageConfigurationInfo extends PolarisStorageConfigurationInfo private static final Pattern ROLE_ARN_PATTERN_COMPILED = Pattern.compile(ROLE_ARN_PATTERN); - // AWS role to be assumed - private final @Nonnull String roleARN; - - // AWS external ID, optional - @JsonProperty(value = "externalId") - private @Nullable String externalId = null; - - /** User ARN for the service principal */ - @JsonProperty(value = "userARN") - private @Nullable String userARN = null; - - /** User ARN for the service principal */ - @JsonProperty(value = "region") - private @Nullable String region = null; - - /** Endpoint URI for S3 API calls */ - @JsonProperty(value = "endpoint") - private @Nullable String endpoint; - - /** Endpoint URI for internal Polaris calls to S3 API */ - @JsonProperty(value = "endpointInternal") - private @Nullable String endpointInternal; - - /** Endpoint URI for STS API calls */ - @JsonProperty(value = "stsEndpoint") - private @Nullable String stsEndpoint; - - /** A flag indicating whether path-style bucket access should be forced in S3 clients. */ - @JsonProperty(value = "pathStyleAccess") - private Boolean pathStyleAccess; - - @JsonCreator - public AwsStorageConfigurationInfo( - @JsonProperty(value = "storageType", required = true) @Nonnull StorageType storageType, - @JsonProperty(value = "allowedLocations", required = true) @Nonnull - List<String> allowedLocations, - @JsonProperty(value = "roleARN", required = true) @Nonnull String roleARN, - @JsonProperty(value = "externalId") @Nullable String externalId, - @JsonProperty(value = "region", required = false) @Nullable String region, - @JsonProperty(value = "endpoint") @Nullable String endpoint, - @JsonProperty(value = "stsEndpoint") @Nullable String stsEndpoint, - @JsonProperty(value = "pathStyleAccess") @Nullable Boolean pathStyleAccess, - @JsonProperty(value = "endpointInternal") @Nullable String endpointInternal) { - super(storageType, allowedLocations); - this.roleARN = roleARN; - this.externalId = externalId; - this.region = region; - this.endpoint = endpoint; - this.stsEndpoint = stsEndpoint; - this.pathStyleAccess = pathStyleAccess; - this.endpointInternal = endpointInternal; - } - - public AwsStorageConfigurationInfo( - @Nonnull StorageType storageType, - @Nonnull List<String> allowedLocations, - @Nonnull String roleARN, - @Nullable String region) { - this(storageType, allowedLocations, roleARN, null, region, null, null, null, null); - } - - public AwsStorageConfigurationInfo( - @Nonnull StorageType storageType, - @Nonnull List<String> allowedLocations, - @Nonnull String roleARN, - @Nullable String externalId, - @Nullable String region) { - this(storageType, allowedLocations, roleARN, externalId, region, null, null, null, null); + @Override + public StorageType getStorageType() { + return StorageType.S3; } @Override @@ -127,76 +69,62 @@ public class AwsStorageConfigurationInfo extends PolarisStorageConfigurationInfo } } - public @Nonnull String getRoleARN() { - return roleARN; - } - - public @Nullable String getExternalId() { - return externalId; - } + public abstract String getRoleARN(); - public void setExternalId(@Nullable String externalId) { - this.externalId = externalId; - } - - public @Nullable String getUserARN() { - return userARN; - } + /** AWS external ID, optional */ + @Nullable + public abstract String getExternalId(); - public void setUserARN(@Nullable String userARN) { - this.userARN = userARN; - } + /** User ARN for the service principal */ + @Nullable + public abstract String getUserARN(); - public @Nullable String getRegion() { - return region; - } + /** AWS region */ + @Nullable + public abstract String getRegion(); - public void setRegion(@Nullable String region) { - this.region = region; - } + /** Endpoint URI for S3 API calls */ + @Nullable + public abstract String getEndpoint(); + /** Internal endpoint URI for S3 API calls */ @Nullable - public String getEndpoint() { - return endpoint; - } + public abstract String getEndpointInternal(); @JsonIgnore @Nullable public URI getEndpointUri() { - return endpoint == null ? null : URI.create(endpoint); + return getEndpoint() == null ? null : URI.create(getEndpoint()); } @JsonIgnore @Nullable public URI getInternalEndpointUri() { - return endpointInternal == null ? getEndpointUri() : URI.create(endpointInternal); + return getEndpointInternal() == null ? getEndpointUri() : URI.create(getEndpointInternal()); } - /** Returns a flag indicating whether path-style bucket access should be forced in S3 clients. */ - public @Nullable Boolean getPathStyleAccess() { - return pathStyleAccess; - } + /** Flag indicating whether path-style bucket access should be forced in S3 clients. */ + public abstract @Nullable Boolean getPathStyleAccess(); + /** Endpoint URI for STS API calls */ @Nullable - public String getStsEndpoint() { - return stsEndpoint; - } + public abstract String getStsEndpoint(); /** Returns the STS endpoint if set, defaulting to {@link #getEndpointUri()} otherwise. */ @JsonIgnore @Nullable public URI getStsEndpointUri() { - return stsEndpoint == null ? getInternalEndpointUri() : URI.create(stsEndpoint); + return getStsEndpoint() == null ? getInternalEndpointUri() : URI.create(getStsEndpoint()); } @JsonIgnore public String getAwsAccountId() { - return parseAwsAccountId(roleARN); + return parseAwsAccountId(getRoleARN()); } @JsonIgnore public String getAwsPartition() { - return parseAwsPartition(roleARN); + return parseAwsPartition(getRoleARN()); } private static String parseAwsAccountId(String arn) { @@ -218,17 +146,4 @@ public class AwsStorageConfigurationInfo extends PolarisStorageConfigurationInfo throw new IllegalArgumentException("ARN does not match the expected role ARN pattern"); } } - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("storageType", getStorageType()) - .add("storageType", getStorageType().name()) - .add("roleARN", roleARN) - .add("userARN", userARN) - .add("externalId", externalId) - .add("allowedLocation", getAllowedLocations()) - .add("region", region) - .toString(); - } } diff --git a/polaris-core/src/main/java/org/apache/polaris/core/storage/azure/AzureStorageConfigurationInfo.java b/polaris-core/src/main/java/org/apache/polaris/core/storage/azure/AzureStorageConfigurationInfo.java index 53695b481..fb4d6c44a 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/storage/azure/AzureStorageConfigurationInfo.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/storage/azure/AzureStorageConfigurationInfo.java @@ -18,36 +18,23 @@ */ package org.apache.polaris.core.storage.azure; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.base.MoreObjects; -import jakarta.annotation.Nonnull; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; import jakarta.annotation.Nullable; -import java.util.List; import java.util.Objects; import org.apache.polaris.core.storage.PolarisStorageConfigurationInfo; +import org.apache.polaris.immutables.PolarisImmutable; /** Azure storage configuration information. */ -public class AzureStorageConfigurationInfo extends PolarisStorageConfigurationInfo { +@PolarisImmutable +@JsonSerialize(as = ImmutableAzureStorageConfigurationInfo.class) +@JsonDeserialize(as = ImmutableAzureStorageConfigurationInfo.class) +@JsonTypeName("AzureStorageConfigurationInfo") +public abstract class AzureStorageConfigurationInfo extends PolarisStorageConfigurationInfo { - // Azure tenant id - private final @Nonnull String tenantId; - - /** The multi tenant app name for the service principal */ - @JsonProperty(value = "multiTenantAppName", required = false) - private @Nullable String multiTenantAppName = null; - - /** The consent url to the Azure permissions request page */ - @JsonProperty(value = "consentUrl", required = false) - private @Nullable String consentUrl = null; - - @JsonCreator - public AzureStorageConfigurationInfo( - @JsonProperty(value = "allowedLocations", required = true) @Nonnull - List<String> allowedLocations, - @JsonProperty(value = "tenantId", required = true) @Nonnull String tenantId) { - super(StorageType.AZURE, allowedLocations); - this.tenantId = tenantId; + public static ImmutableAzureStorageConfigurationInfo.Builder builder() { + return ImmutableAzureStorageConfigurationInfo.builder(); } @Override @@ -55,36 +42,21 @@ public class AzureStorageConfigurationInfo extends PolarisStorageConfigurationIn return "org.apache.iceberg.azure.adlsv2.ADLSFileIO"; } - public @Nonnull String getTenantId() { - return tenantId; - } - - public @Nullable String getMultiTenantAppName() { - return multiTenantAppName; - } - - public void setMultiTenantAppName(@Nullable String multiTenantAppName) { - this.multiTenantAppName = multiTenantAppName; + @Override + public StorageType getStorageType() { + return StorageType.AZURE; } - public @Nullable String getConsentUrl() { - return consentUrl; - } + /** Azure tenant ID. */ + public abstract String getTenantId(); - public void setConsentUrl(@Nullable String consentUrl) { - this.consentUrl = consentUrl; - } + /** The multi tenant app name for the service principal */ + @Nullable + public abstract String getMultiTenantAppName(); - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("storageType", getStorageType()) - .add("tenantId", tenantId) - .add("allowedLocation", getAllowedLocations()) - .add("multiTenantAppName", multiTenantAppName) - .add("consentUrl", consentUrl) - .toString(); - } + /** The consent url to the Azure permissions request page */ + @Nullable + public abstract String getConsentUrl(); @Override public void validatePrefixForStorageType(String loc) { diff --git a/polaris-core/src/main/java/org/apache/polaris/core/storage/gcp/GcpStorageConfigurationInfo.java b/polaris-core/src/main/java/org/apache/polaris/core/storage/gcp/GcpStorageConfigurationInfo.java index e664d4bd3..13f2da66d 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/storage/gcp/GcpStorageConfigurationInfo.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/storage/gcp/GcpStorageConfigurationInfo.java @@ -18,26 +18,22 @@ */ package org.apache.polaris.core.storage.gcp; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.base.MoreObjects; -import jakarta.annotation.Nonnull; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; import jakarta.annotation.Nullable; -import java.util.List; import org.apache.polaris.core.storage.PolarisStorageConfigurationInfo; +import org.apache.polaris.immutables.PolarisImmutable; /** Gcp storage storage configuration information. */ -public class GcpStorageConfigurationInfo extends PolarisStorageConfigurationInfo { - - /** The gcp service account */ - @JsonProperty(value = "gcpServiceAccount", required = false) - private @Nullable String gcpServiceAccount = null; - - @JsonCreator - public GcpStorageConfigurationInfo( - @JsonProperty(value = "allowedLocations", required = true) @Nonnull - List<String> allowedLocations) { - super(StorageType.GCS, allowedLocations); +@PolarisImmutable +@JsonSerialize(as = ImmutableGcpStorageConfigurationInfo.class) +@JsonDeserialize(as = ImmutableGcpStorageConfigurationInfo.class) +@JsonTypeName("GcpStorageConfigurationInfo") +public abstract class GcpStorageConfigurationInfo extends PolarisStorageConfigurationInfo { + + public static ImmutableGcpStorageConfigurationInfo.Builder builder() { + return ImmutableGcpStorageConfigurationInfo.builder(); } @Override @@ -45,20 +41,12 @@ public class GcpStorageConfigurationInfo extends PolarisStorageConfigurationInfo return "org.apache.iceberg.gcp.gcs.GCSFileIO"; } - public void setGcpServiceAccount(@Nullable String gcpServiceAccount) { - this.gcpServiceAccount = gcpServiceAccount; - } - - public @Nullable String getGcpServiceAccount() { - return gcpServiceAccount; - } - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("storageType", getStorageType()) - .add("allowedLocation", getAllowedLocations()) - .add("gcpServiceAccount", gcpServiceAccount) - .toString(); + public StorageType getStorageType() { + return StorageType.GCS; } + + /** The gcp service account */ + @Nullable + public abstract String getGcpServiceAccount(); } diff --git a/polaris-core/src/test/java/org/apache/polaris/core/storage/InMemoryStorageIntegrationTest.java b/polaris-core/src/test/java/org/apache/polaris/core/storage/InMemoryStorageIntegrationTest.java index 47a760f03..1bf6e4bb2 100644 --- a/polaris-core/src/test/java/org/apache/polaris/core/storage/InMemoryStorageIntegrationTest.java +++ b/polaris-core/src/test/java/org/apache/polaris/core/storage/InMemoryStorageIntegrationTest.java @@ -20,7 +20,6 @@ package org.apache.polaris.core.storage; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; -import java.util.List; import java.util.Map; import java.util.Set; import org.apache.polaris.core.config.PolarisConfigurationStore; @@ -48,14 +47,14 @@ class InMemoryStorageIntegrationTest { Map<String, Map<PolarisStorageActions, PolarisStorageIntegration.ValidationResult>> result = storage.validateAccessToLocations( realmConfig, - new AwsStorageConfigurationInfo( - PolarisStorageConfigurationInfo.StorageType.S3, - List.of( + AwsStorageConfigurationInfo.builder() + .addAllowedLocations( allowedScheme + "://bucket/path/to/warehouse", allowedScheme + "://bucket/anotherpath/to/warehouse", - allowedScheme + "://bucket2/warehouse/"), - "arn:aws:iam::012345678901:role/jdoe", - "us-east-2"), + allowedScheme + "://bucket2/warehouse/") + .roleARN("arn:aws:iam::012345678901:role/jdoe") + .region("us-east-2") + .build(), Set.of(PolarisStorageActions.READ), Set.of( locationScheme + "://bucket/path/to/warehouse/namespace/table", @@ -83,11 +82,11 @@ class InMemoryStorageIntegrationTest { @Test public void testAwsAccountIdParsing() { AwsStorageConfigurationInfo awsConfig = - new AwsStorageConfigurationInfo( - PolarisStorageConfigurationInfo.StorageType.S3, - List.of("s3://bucket/path/to/warehouse"), - "arn:aws:iam::012345678901:role/jdoe", - "us-east-2"); + AwsStorageConfigurationInfo.builder() + .addAllowedLocation("s3://bucket/path/to/warehouse") + .roleARN("arn:aws:iam::012345678901:role/jdoe") + .region("us-east-2") + .build(); String expectedAccountId = "012345678901"; String actualAccountId = awsConfig.getAwsAccountId(); @@ -105,7 +104,7 @@ class InMemoryStorageIntegrationTest { Map<String, Map<PolarisStorageActions, PolarisStorageIntegration.ValidationResult>> result = storage.validateAccessToLocations( realmConfig, - new FileStorageConfigurationInfo(List.of("file://", "*")), + FileStorageConfigurationInfo.builder().addAllowedLocations("file://", "*").build(), Set.of(PolarisStorageActions.READ), Set.of( s3Scheme + "://bucket/path/to/warehouse/namespace/table", @@ -147,11 +146,10 @@ class InMemoryStorageIntegrationTest { Map<String, Map<PolarisStorageActions, PolarisStorageIntegration.ValidationResult>> result = storage.validateAccessToLocations( realmConfig, - new AwsStorageConfigurationInfo( - PolarisStorageConfigurationInfo.StorageType.S3, - List.of(), - "arn:aws:iam::012345678901:role/jdoe", - "us-east-2"), + AwsStorageConfigurationInfo.builder() + .roleARN("arn:aws:iam::012345678901:role/jdoe") + .region("us-east-2") + .build(), Set.of(PolarisStorageActions.READ), Set.of( "s3://bucket/path/to/warehouse/namespace/table", @@ -184,11 +182,11 @@ class InMemoryStorageIntegrationTest { Map<String, Map<PolarisStorageActions, PolarisStorageIntegration.ValidationResult>> result = storage.validateAccessToLocations( realmConfig, - new AwsStorageConfigurationInfo( - PolarisStorageConfigurationInfo.StorageType.S3, - List.of("s3://bucket/path/to/warehouse"), - "arn:aws:iam::012345678901:role/jdoe", - "us-east-2"), + AwsStorageConfigurationInfo.builder() + .addAllowedLocation("s3://bucket/path/to/warehouse") + .roleARN("arn:aws:iam::012345678901:role/jdoe") + .region("us-east-2") + .build(), Set.of(PolarisStorageActions.READ), // trying to read a prefix under the allowed location Set.of("s3://bucket/path/to")); diff --git a/polaris-core/src/test/java/org/apache/polaris/core/storage/aws/AwsStorageConfigurationInfoTest.java b/polaris-core/src/test/java/org/apache/polaris/core/storage/aws/AwsStorageConfigurationInfoTest.java index 8605b8142..faca7193f 100644 --- a/polaris-core/src/test/java/org/apache/polaris/core/storage/aws/AwsStorageConfigurationInfoTest.java +++ b/polaris-core/src/test/java/org/apache/polaris/core/storage/aws/AwsStorageConfigurationInfoTest.java @@ -19,53 +19,44 @@ package org.apache.polaris.core.storage.aws; -import static org.apache.polaris.core.storage.PolarisStorageConfigurationInfo.StorageType.S3; import static org.assertj.core.api.Assertions.assertThat; import java.net.URI; -import java.util.List; import org.junit.jupiter.api.Test; public class AwsStorageConfigurationInfoTest { - private static AwsStorageConfigurationInfo config(String endpoint, String stsEndpoint) { - return config(endpoint, stsEndpoint, false); - } - - private static AwsStorageConfigurationInfo config( - String endpoint, String stsEndpoint, Boolean pathStyle) { - return config(endpoint, stsEndpoint, pathStyle, null); - } - - private static AwsStorageConfigurationInfo config( - String endpoint, String stsEndpoint, Boolean pathStyle, String internalEndpoint) { - return new AwsStorageConfigurationInfo( - S3, List.of(), "role", null, null, endpoint, stsEndpoint, pathStyle, internalEndpoint); - } - @Test public void testStsEndpoint() { - assertThat(config(null, null)) + assertThat(newBuilder().build()) .extracting( AwsStorageConfigurationInfo::getEndpointUri, AwsStorageConfigurationInfo::getStsEndpointUri) .containsExactly(null, null); - assertThat(config(null, "http://sts.example.com")) + assertThat(newBuilder().stsEndpoint("http://sts.example.com").build()) .extracting( AwsStorageConfigurationInfo::getEndpointUri, AwsStorageConfigurationInfo::getStsEndpointUri) .containsExactly(null, URI.create("http://sts.example.com")); - assertThat(config("http://s3.example.com", null)) + assertThat(newBuilder().endpoint("http://s3.example.com").build()) .extracting( AwsStorageConfigurationInfo::getEndpointUri, AwsStorageConfigurationInfo::getStsEndpointUri) .containsExactly(URI.create("http://s3.example.com"), URI.create("http://s3.example.com")); - assertThat(config("http://s3.example.com", "http://sts.example.com")) + assertThat( + newBuilder() + .endpoint("http://s3.example.com") + .stsEndpoint("http://sts.example.com") + .build()) .extracting( AwsStorageConfigurationInfo::getEndpointUri, AwsStorageConfigurationInfo::getStsEndpointUri) .containsExactly(URI.create("http://s3.example.com"), URI.create("http://sts.example.com")); - assertThat(config("http://s3.example.com", null, false, "http://int.example.com")) + assertThat( + newBuilder() + .endpoint("http://s3.example.com") + .endpointInternal("http://int.example.com") + .build()) .extracting( AwsStorageConfigurationInfo::getEndpointUri, AwsStorageConfigurationInfo::getStsEndpointUri, @@ -76,31 +67,42 @@ public class AwsStorageConfigurationInfoTest { URI.create("http://int.example.com")); } + private static ImmutableAwsStorageConfigurationInfo.Builder newBuilder() { + return AwsStorageConfigurationInfo.builder().roleARN("role"); + } + @Test public void testInternalEndpoint() { - assertThat(config(null, null)) + assertThat(newBuilder().build()) .extracting( AwsStorageConfigurationInfo::getEndpointUri, AwsStorageConfigurationInfo::getInternalEndpointUri) .containsExactly(null, null); - assertThat(config(null, "http://sts.example.com")) + assertThat(newBuilder().stsEndpoint("http://sts.example.com").build()) .extracting( AwsStorageConfigurationInfo::getEndpointUri, AwsStorageConfigurationInfo::getInternalEndpointUri) .containsExactly(null, null); - assertThat(config("http://s3.example.com", null)) + assertThat(newBuilder().endpoint("http://s3.example.com").build()) .extracting( AwsStorageConfigurationInfo::getEndpointUri, AwsStorageConfigurationInfo::getInternalEndpointUri) .containsExactly(URI.create("http://s3.example.com"), URI.create("http://s3.example.com")); assertThat( - config( - "http://s3.example.com", "http://sts.example.com", false, "http://int.example.com")) + newBuilder() + .endpoint("http://s3.example.com") + .stsEndpoint("http://sts.example.com") + .endpointInternal("http://int.example.com") + .build()) .extracting( AwsStorageConfigurationInfo::getEndpointUri, AwsStorageConfigurationInfo::getInternalEndpointUri) .containsExactly(URI.create("http://s3.example.com"), URI.create("http://int.example.com")); - assertThat(config(null, "http://sts.example.com", false, "http://int.example.com")) + assertThat( + newBuilder() + .stsEndpoint("http://sts.example.com") + .endpointInternal("http://int.example.com") + .build()) .extracting( AwsStorageConfigurationInfo::getEndpointUri, AwsStorageConfigurationInfo::getInternalEndpointUri) @@ -109,8 +111,8 @@ public class AwsStorageConfigurationInfoTest { @Test public void testPathStyleAccess() { - assertThat(config(null, null, null).getPathStyleAccess()).isNull(); - assertThat(config(null, null, false).getPathStyleAccess()).isFalse(); - assertThat(config(null, null, true).getPathStyleAccess()).isTrue(); + assertThat(newBuilder().pathStyleAccess(null).build().getPathStyleAccess()).isNull(); + assertThat(newBuilder().pathStyleAccess(false).build().getPathStyleAccess()).isFalse(); + assertThat(newBuilder().pathStyleAccess(true).build().getPathStyleAccess()).isTrue(); } } diff --git a/polaris-core/src/test/java/org/apache/polaris/service/storage/PolarisStorageConfigurationInfoTest.java b/polaris-core/src/test/java/org/apache/polaris/service/storage/PolarisStorageConfigurationInfoTest.java new file mode 100644 index 000000000..fe15147ed --- /dev/null +++ b/polaris-core/src/test/java/org/apache/polaris/service/storage/PolarisStorageConfigurationInfoTest.java @@ -0,0 +1,118 @@ +/* + * 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.polaris.service.storage; + +import static org.junit.jupiter.params.provider.Arguments.arguments; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.stream.Stream; +import org.apache.polaris.core.storage.FileStorageConfigurationInfo; +import org.apache.polaris.core.storage.PolarisStorageConfigurationInfo; +import org.apache.polaris.core.storage.aws.AwsStorageConfigurationInfo; +import org.apache.polaris.core.storage.azure.AzureStorageConfigurationInfo; +import org.apache.polaris.core.storage.gcp.GcpStorageConfigurationInfo; +import org.assertj.core.api.SoftAssertions; +import org.assertj.core.api.junit.jupiter.InjectSoftAssertions; +import org.assertj.core.api.junit.jupiter.SoftAssertionsExtension; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +@ExtendWith(SoftAssertionsExtension.class) +public class PolarisStorageConfigurationInfoTest { + @InjectSoftAssertions protected SoftAssertions soft; + + private static ObjectMapper mapper; + + @BeforeAll + public static void setup() { + mapper = new ObjectMapper(); + } + + @ParameterizedTest + @MethodSource + public void reserialization(PolarisStorageConfigurationInfo configInfo, String serialized) + throws Exception { + var jsonStr = configInfo.serialize(); + + var asJsonNode = mapper.readValue(jsonStr, JsonNode.class); + var serializedJsonNode = mapper.readValue(serialized, JsonNode.class); + soft.assertThat(asJsonNode).isEqualTo(serializedJsonNode); + + var deserialized = mapper.readValue(serialized, PolarisStorageConfigurationInfo.class); + soft.assertThat(deserialized).isEqualTo(configInfo); + + var reserialized = mapper.readValue(jsonStr, PolarisStorageConfigurationInfo.class); + soft.assertThat(reserialized).isEqualTo(configInfo); + } + + static Stream<Arguments> reserialization() { + return Stream.of( + arguments( + AwsStorageConfigurationInfo.builder() + .addAllowedLocations("s3://foo/bar", "s3://no/where") + .roleARN("arn:foo") + .region("no-where-1") + .build(), + "{\"@type\":\"AwsStorageConfigurationInfo\",\"storageType\":\"S3\",\"allowedLocations\":[\"s3://foo/bar\",\"s3://no/where\"],\"roleARN\":\"arn:foo\",\"region\":\"no-where-1\",\"fileIoImplClassName\":\"org.apache.iceberg.aws.s3.S3FileIO\"}"), + arguments( + AwsStorageConfigurationInfo.builder() + .addAllowedLocations("s3://foo/bar", "s3://no/where") + .region("no-where-1") + .roleARN("arn:foo") + .externalId("external-id") + .build(), + "{\"@type\":\"AwsStorageConfigurationInfo\",\"storageType\":\"S3\",\"allowedLocations\":[\"s3://foo/bar\",\"s3://no/where\"],\"roleARN\":\"arn:foo\",\"externalId\":\"external-id\",\"region\":\"no-where-1\",\"fileIoImplClassName\":\"org.apache.iceberg.aws.s3.S3FileIO\"}"), + arguments( + AwsStorageConfigurationInfo.builder() + .addAllowedLocations("s3://foo/bar", "s3://no/where") + .region("no-where-1") + .roleARN("arn:foo") + .externalId("external-id") + .endpoint("http://127.9.9.9/") + .stsEndpoint("http://127.9.9.9/sts/") + .endpointInternal("http://127.8.8.8/internal/") + .pathStyleAccess(true) + .build(), + "{\"@type\":\"AwsStorageConfigurationInfo\",\"storageType\":\"S3\",\"allowedLocations\":[\"s3://foo/bar\",\"s3://no/where\"],\"roleARN\":\"arn:foo\",\"externalId\":\"external-id\",\"region\":\"no-where-1\",\"endpoint\":\"http://127.9.9.9/\",\"stsEndpoint\":\"http://127.9.9.9/sts/\",\"endpointInternal\":\"http://127.8.8.8/internal/\",\"pathStyleAccess\":true,\"fileIoImplClassName\":\"org.apache.iceberg.aws.s3.S3FileIO\"}"), + // + arguments( + GcpStorageConfigurationInfo.builder() + .addAllowedLocations("gs://foo/bar", "gs://meep/moo") + .build(), + "{\"@type\":\"GcpStorageConfigurationInfo\",\"allowedLocations\":[\"gs://foo/bar\",\"gs://meep/moo\"],\"storageType\":\"GCS\",\"fileIoImplClassName\":\"org.apache.iceberg.gcp.gcs.GCSFileIO\"}"), + // + arguments( + AzureStorageConfigurationInfo.builder() + .addAllowedLocations("abfs://f...@bar.baz/", "abfss://b...@meep.buzz/") + .tenantId("tenant-id") + .build(), + "{\"@type\":\"AzureStorageConfigurationInfo\",\"allowedLocations\":[\"abfs://f...@bar.baz/\",\"abfss://b...@meep.buzz/\"],\"tenantId\":\"tenant-id\",\"storageType\":\"AZURE\",\"fileIoImplClassName\":\"org.apache.iceberg.azure.adlsv2.ADLSFileIO\"}"), + // + arguments( + FileStorageConfigurationInfo.builder() + .addAllowedLocations("file:///tmp/bar", "file:///meep/moo") + .build(), + "{\"@type\":\"FileStorageConfigurationInfo\",\"allowedLocations\":[\"file:///tmp/bar\",\"file:///meep/moo\"],\"storageType\":\"FILE\",\"fileIoImplClassName\":\"org.apache.iceberg.hadoop.HadoopFileIO\"}")); + } +} diff --git a/polaris-core/src/test/java/org/apache/polaris/service/storage/aws/AwsCredentialsStorageIntegrationTest.java b/polaris-core/src/test/java/org/apache/polaris/service/storage/aws/AwsCredentialsStorageIntegrationTest.java index 604086a29..10ba4b908 100644 --- a/polaris-core/src/test/java/org/apache/polaris/service/storage/aws/AwsCredentialsStorageIntegrationTest.java +++ b/polaris-core/src/test/java/org/apache/polaris/service/storage/aws/AwsCredentialsStorageIntegrationTest.java @@ -18,7 +18,6 @@ */ package org.apache.polaris.service.storage.aws; -import static org.apache.polaris.core.storage.PolarisStorageConfigurationInfo.StorageType.S3; import static org.assertj.core.api.Assertions.assertThat; import jakarta.annotation.Nonnull; @@ -27,7 +26,6 @@ import java.util.List; import java.util.Set; import org.apache.polaris.core.storage.AccessConfig; import org.apache.polaris.core.storage.BaseStorageIntegrationTest; -import org.apache.polaris.core.storage.PolarisStorageConfigurationInfo; import org.apache.polaris.core.storage.StorageAccessProperty; import org.apache.polaris.core.storage.aws.AwsCredentialsStorageIntegration; import org.apache.polaris.core.storage.aws.AwsStorageConfigurationInfo; @@ -87,8 +85,11 @@ class AwsCredentialsStorageIntegrationTest extends BaseStorageIntegrationTest { String warehouseDir = scheme + "://bucket/path/to/warehouse"; AccessConfig accessConfig = new AwsCredentialsStorageIntegration( - new AwsStorageConfigurationInfo( - S3, List.of(warehouseDir), roleARN, externalId, null), + AwsStorageConfigurationInfo.builder() + .addAllowedLocation(warehouseDir) + .roleARN(roleARN) + .externalId(externalId) + .build(), stsClient) .getSubscopedCreds( EMPTY_REALM_CONFIG, @@ -108,7 +109,6 @@ class AwsCredentialsStorageIntegrationTest extends BaseStorageIntegrationTest { @ParameterizedTest @ValueSource(strings = {AWS_PARTITION, "aws-cn", "aws-us-gov"}) public void testGetSubscopedCredsInlinePolicy(String awsPartition) { - PolarisStorageConfigurationInfo.StorageType storageType = S3; String roleARN; String region; switch (awsPartition) { @@ -231,12 +231,12 @@ class AwsCredentialsStorageIntegrationTest extends BaseStorageIntegrationTest { Assertions.assertThatThrownBy( () -> new AwsCredentialsStorageIntegration( - new AwsStorageConfigurationInfo( - storageType, - List.of(s3Path(bucket, warehouseKeyPrefix)), - roleARN, - externalId, - region), + AwsStorageConfigurationInfo.builder() + .addAllowedLocation(s3Path(bucket, warehouseKeyPrefix)) + .roleARN(roleARN) + .externalId(externalId) + .region(region) + .build(), stsClient) .getSubscopedCreds( EMPTY_REALM_CONFIG, @@ -249,12 +249,12 @@ class AwsCredentialsStorageIntegrationTest extends BaseStorageIntegrationTest { case "aws-us-gov": AccessConfig accessConfig = new AwsCredentialsStorageIntegration( - new AwsStorageConfigurationInfo( - storageType, - List.of(s3Path(bucket, warehouseKeyPrefix)), - roleARN, - externalId, - region), + AwsStorageConfigurationInfo.builder() + .addAllowedLocation(s3Path(bucket, warehouseKeyPrefix)) + .roleARN(roleARN) + .externalId(externalId) + .region(region) + .build(), stsClient) .getSubscopedCreds( EMPTY_REALM_CONFIG, @@ -349,12 +349,12 @@ class AwsCredentialsStorageIntegrationTest extends BaseStorageIntegrationTest { }); AccessConfig accessConfig = new AwsCredentialsStorageIntegration( - new AwsStorageConfigurationInfo( - S3, - List.of(s3Path(bucket, warehouseKeyPrefix)), - roleARN, - externalId, - "us-east-2"), + AwsStorageConfigurationInfo.builder() + .addAllowedLocation(s3Path(bucket, warehouseKeyPrefix)) + .roleARN(roleARN) + .externalId(externalId) + .region("us-east-2") + .build(), stsClient) .getSubscopedCreds( EMPTY_REALM_CONFIG, @@ -443,12 +443,12 @@ class AwsCredentialsStorageIntegrationTest extends BaseStorageIntegrationTest { }); AccessConfig accessConfig = new AwsCredentialsStorageIntegration( - new AwsStorageConfigurationInfo( - S3, - List.of(s3Path(bucket, warehouseKeyPrefix)), - roleARN, - externalId, - "us-east-2"), + AwsStorageConfigurationInfo.builder() + .addAllowedLocation(s3Path(bucket, warehouseKeyPrefix)) + .roleARN(roleARN) + .externalId(externalId) + .region("us-east-2") + .build(), stsClient) .getSubscopedCreds( EMPTY_REALM_CONFIG, @@ -509,12 +509,12 @@ class AwsCredentialsStorageIntegrationTest extends BaseStorageIntegrationTest { }); AccessConfig accessConfig = new AwsCredentialsStorageIntegration( - new AwsStorageConfigurationInfo( - S3, - List.of(s3Path(bucket, warehouseKeyPrefix)), - roleARN, - externalId, - "us-east-2"), + AwsStorageConfigurationInfo.builder() + .addAllowedLocation(s3Path(bucket, warehouseKeyPrefix)) + .roleARN(roleARN) + .externalId(externalId) + .region("us-east-2") + .build(), stsClient) .getSubscopedCreds(EMPTY_REALM_CONFIG, true, /* allowList = true */ Set.of(), Set.of()); assertThat(accessConfig.credentials()) @@ -546,12 +546,12 @@ class AwsCredentialsStorageIntegrationTest extends BaseStorageIntegrationTest { Assertions.assertThatThrownBy( () -> new AwsCredentialsStorageIntegration( - new AwsStorageConfigurationInfo( - S3, - List.of(s3Path(bucket, warehouseKeyPrefix)), - roleARN, - externalId, - clientRegion), + AwsStorageConfigurationInfo.builder() + .addAllowedLocation(s3Path(bucket, warehouseKeyPrefix)) + .roleARN(roleARN) + .externalId(externalId) + .region(clientRegion) + .build(), stsClient) .getSubscopedCreds( EMPTY_REALM_CONFIG, true, /* allowList = true */ Set.of(), Set.of())) @@ -561,12 +561,12 @@ class AwsCredentialsStorageIntegrationTest extends BaseStorageIntegrationTest { case "aws-us-gov": AccessConfig accessConfig = new AwsCredentialsStorageIntegration( - new AwsStorageConfigurationInfo( - S3, - List.of(s3Path(bucket, warehouseKeyPrefix)), - roleARN, - externalId, - clientRegion), + AwsStorageConfigurationInfo.builder() + .addAllowedLocation(s3Path(bucket, warehouseKeyPrefix)) + .roleARN(roleARN) + .externalId(externalId) + .region(clientRegion) + .build(), stsClient) .getSubscopedCreds( EMPTY_REALM_CONFIG, true, /* allowList = true */ Set.of(), Set.of()); @@ -597,8 +597,11 @@ class AwsCredentialsStorageIntegrationTest extends BaseStorageIntegrationTest { case AWS_PARTITION: AccessConfig accessConfig = new AwsCredentialsStorageIntegration( - new AwsStorageConfigurationInfo( - S3, List.of(s3Path(bucket, warehouseKeyPrefix)), roleARN, externalId, null), + AwsStorageConfigurationInfo.builder() + .addAllowedLocation(s3Path(bucket, warehouseKeyPrefix)) + .roleARN(roleARN) + .externalId(externalId) + .build(), stsClient) .getSubscopedCreds( EMPTY_REALM_CONFIG, true, /* allowList = true */ Set.of(), Set.of()); @@ -611,12 +614,11 @@ class AwsCredentialsStorageIntegrationTest extends BaseStorageIntegrationTest { Assertions.assertThatThrownBy( () -> new AwsCredentialsStorageIntegration( - new AwsStorageConfigurationInfo( - PolarisStorageConfigurationInfo.StorageType.S3, - List.of(s3Path(bucket, warehouseKeyPrefix)), - roleARN, - externalId, - null), + AwsStorageConfigurationInfo.builder() + .addAllowedLocation(s3Path(bucket, warehouseKeyPrefix)) + .roleARN(roleARN) + .externalId(externalId) + .build(), stsClient) .getSubscopedCreds( EMPTY_REALM_CONFIG, true, /* allowList = true */ Set.of(), Set.of())) diff --git a/polaris-core/src/test/java/org/apache/polaris/service/storage/azure/AzureCredentialStorageIntegrationTest.java b/polaris-core/src/test/java/org/apache/polaris/service/storage/azure/AzureCredentialStorageIntegrationTest.java index d93dcc63f..768f4c330 100644 --- a/polaris-core/src/test/java/org/apache/polaris/service/storage/azure/AzureCredentialStorageIntegrationTest.java +++ b/polaris-core/src/test/java/org/apache/polaris/service/storage/azure/AzureCredentialStorageIntegrationTest.java @@ -340,11 +340,12 @@ public class AzureCredentialStorageIntegrationTest extends BaseStorageIntegratio private AccessConfig subscopedCredsForOperations( List<String> allowedReadLoc, List<String> allowedWriteLoc, boolean allowListAction) { - List<String> allowedLoc = new ArrayList<>(); - allowedLoc.addAll(allowedReadLoc); - allowedLoc.addAll(allowedWriteLoc); AzureStorageConfigurationInfo azureConfig = - new AzureStorageConfigurationInfo(allowedLoc, tenantId); + AzureStorageConfigurationInfo.builder() + .addAllAllowedLocations(allowedReadLoc) + .addAllAllowedLocations(allowedWriteLoc) + .tenantId(tenantId) + .build(); AzureCredentialsStorageIntegration azureCredsIntegration = new AzureCredentialsStorageIntegration(azureConfig); return azureCredsIntegration.getSubscopedCreds( diff --git a/polaris-core/src/test/java/org/apache/polaris/service/storage/gcp/GcpCredentialsStorageIntegrationTest.java b/polaris-core/src/test/java/org/apache/polaris/service/storage/gcp/GcpCredentialsStorageIntegrationTest.java index e0985199b..da890627e 100644 --- a/polaris-core/src/test/java/org/apache/polaris/service/storage/gcp/GcpCredentialsStorageIntegrationTest.java +++ b/polaris-core/src/test/java/org/apache/polaris/service/storage/gcp/GcpCredentialsStorageIntegrationTest.java @@ -37,7 +37,6 @@ import com.google.cloud.storage.Storage; import com.google.cloud.storage.StorageException; import com.google.cloud.storage.StorageOptions; import java.io.IOException; -import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashSet; @@ -157,10 +156,11 @@ class GcpCredentialsStorageIntegrationTest extends BaseStorageIntegrationTest { private AccessConfig subscopedCredsForOperations( List<String> allowedReadLoc, List<String> allowedWriteLoc, boolean allowListAction) throws IOException { - List<String> allowedLoc = new ArrayList<>(); - allowedLoc.addAll(allowedReadLoc); - allowedLoc.addAll(allowedWriteLoc); - GcpStorageConfigurationInfo gcpConfig = new GcpStorageConfigurationInfo(allowedLoc); + GcpStorageConfigurationInfo gcpConfig = + GcpStorageConfigurationInfo.builder() + .addAllAllowedLocations(allowedReadLoc) + .addAllAllowedLocations(allowedWriteLoc) + .build(); GcpCredentialsStorageIntegration gcpCredsIntegration = new GcpCredentialsStorageIntegration( gcpConfig,