This is an automated email from the ASF dual-hosted git repository.
collado 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 91c69cd5 Fix GCP and Azure storage configuration to store missing
properties (#370)
91c69cd5 is described below
commit 91c69cd5c5e0265be4285993ba8b893f836f3656
Author: Michael Collado <[email protected]>
AuthorDate: Wed Oct 16 09:33:05 2024 -0700
Fix GCP and Azure storage configuration to store missing properties (#370)
* Fix GCP and Azure storage configuration to store missing properties
* Move aws config to its own variable
* Change gcp token creation to use Optional
---
.../apache/polaris/core/entity/CatalogEntity.java | 16 +++--
.../apache/polaris/service/PolarisApplication.java | 3 +-
.../service/config/PolarisApplicationConfig.java | 64 +++++++++++++++++++
.../PolarisStorageIntegrationProviderImpl.java | 23 +++----
.../service/admin/PolarisAuthzTestBase.java | 6 +-
.../admin/PolarisServiceImplIntegrationTest.java | 72 +++++++++++++++++++++-
.../catalog/BasePolarisCatalogViewTest.java | 6 +-
7 files changed, 169 insertions(+), 21 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 924b2077..fea6b746 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
@@ -239,22 +239,30 @@ public class CatalogEntity extends PolarisEntity {
switch (storageConfigModel.getStorageType()) {
case S3:
AwsStorageConfigInfo awsConfigModel = (AwsStorageConfigInfo)
storageConfigModel;
- config =
+ AwsStorageConfigurationInfo awsConfig =
new AwsStorageConfigurationInfo(
PolarisStorageConfigurationInfo.StorageType.S3,
new ArrayList<>(allowedLocations),
awsConfigModel.getRoleArn(),
awsConfigModel.getExternalId());
- ((AwsStorageConfigurationInfo)
config).validateArn(awsConfigModel.getRoleArn());
+ awsConfig.validateArn(awsConfigModel.getRoleArn());
+ config = awsConfig;
break;
case AZURE:
AzureStorageConfigInfo azureConfigModel = (AzureStorageConfigInfo)
storageConfigModel;
- config =
+ AzureStorageConfigurationInfo azureconfigInfo =
new AzureStorageConfigurationInfo(
new ArrayList<>(allowedLocations),
azureConfigModel.getTenantId());
+
azureconfigInfo.setMultiTenantAppName(azureConfigModel.getMultiTenantAppName());
+ azureconfigInfo.setConsentUrl(azureConfigModel.getConsentUrl());
+ config = azureconfigInfo;
break;
case GCS:
- config = new GcpStorageConfigurationInfo(new
ArrayList<>(allowedLocations));
+ GcpStorageConfigurationInfo gcpConfig =
+ new GcpStorageConfigurationInfo(new
ArrayList<>(allowedLocations));
+ gcpConfig.setGcpServiceAccount(
+ ((GcpStorageConfigInfo)
storageConfigModel).getGcsServiceAccount());
+ config = gcpConfig;
break;
case FILE:
config = new FileStorageConfigurationInfo(new
ArrayList<>(allowedLocations));
diff --git
a/polaris-service/src/main/java/org/apache/polaris/service/PolarisApplication.java
b/polaris-service/src/main/java/org/apache/polaris/service/PolarisApplication.java
index dd0597b8..143d3dae 100644
---
a/polaris-service/src/main/java/org/apache/polaris/service/PolarisApplication.java
+++
b/polaris-service/src/main/java/org/apache/polaris/service/PolarisApplication.java
@@ -165,7 +165,8 @@ public class PolarisApplication extends
Application<PolarisApplicationConfig> {
stsClientBuilder.credentialsProvider(awsCredentialsProvider);
}
return stsClientBuilder.build();
- }));
+ },
+ configuration.getGcpCredentialsProvider()));
PolarisMetricRegistry polarisMetricRegistry =
new PolarisMetricRegistry(new
PrometheusMeterRegistry(PrometheusConfig.DEFAULT));
diff --git
a/polaris-service/src/main/java/org/apache/polaris/service/config/PolarisApplicationConfig.java
b/polaris-service/src/main/java/org/apache/polaris/service/config/PolarisApplicationConfig.java
index e623f253..065b6d8f 100644
---
a/polaris-service/src/main/java/org/apache/polaris/service/config/PolarisApplicationConfig.java
+++
b/polaris-service/src/main/java/org/apache/polaris/service/config/PolarisApplicationConfig.java
@@ -19,11 +19,17 @@
package org.apache.polaris.service.config;
import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.auth.oauth2.AccessToken;
+import com.google.auth.oauth2.GoogleCredentials;
import com.google.common.base.Preconditions;
import io.dropwizard.core.Configuration;
+import java.io.IOException;
+import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
+import java.util.function.Supplier;
import javax.annotation.Nullable;
import org.apache.commons.lang3.StringUtils;
import org.apache.polaris.core.PolarisConfigurationStore;
@@ -59,6 +65,8 @@ public class PolarisApplicationConfig extends Configuration {
private FileIOFactory fileIOFactory;
private RateLimiter rateLimiter;
+ private AccessToken gcpAccessToken;
+
public static final long REQUEST_BODY_BYTES_NO_LIMIT = -1;
private long maxRequestBodyBytes = REQUEST_BODY_BYTES_NO_LIMIT;
@@ -208,4 +216,60 @@ public class PolarisApplicationConfig extends
Configuration {
public void setDefaultRealms(List<String> defaultRealms) {
this.defaultRealms = defaultRealms;
}
+
+ public Supplier<GoogleCredentials> getGcpCredentialsProvider() {
+ return () ->
+ Optional.ofNullable(gcpAccessToken)
+ .map(GoogleCredentials::create)
+ .orElseGet(
+ () -> {
+ try {
+ return GoogleCredentials.getApplicationDefault();
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to get GCP
credentials", e);
+ }
+ });
+ }
+
+ @JsonProperty("gcp_credentials")
+ void setGcpCredentials(GcpAccessToken token) {
+ this.gcpAccessToken =
+ new AccessToken(
+ token.getAccessToken(),
+ new Date(System.currentTimeMillis() + token.getExpiresIn() *
1000));
+ }
+
+ /**
+ * A static AccessToken representation used to store a static token and
expiration date. This
+ * should strictly be used for testing.
+ */
+ static class GcpAccessToken {
+ private String accessToken;
+ private long expiresIn;
+
+ public GcpAccessToken() {}
+
+ public GcpAccessToken(String accessToken, long expiresIn) {
+ this.accessToken = accessToken;
+ this.expiresIn = expiresIn;
+ }
+
+ public String getAccessToken() {
+ return accessToken;
+ }
+
+ @JsonProperty("access_token")
+ public void setAccessToken(String accessToken) {
+ this.accessToken = accessToken;
+ }
+
+ public long getExpiresIn() {
+ return expiresIn;
+ }
+
+ @JsonProperty("expires_in")
+ public void setExpiresIn(long expiresIn) {
+ this.expiresIn = expiresIn;
+ }
+ }
}
diff --git
a/polaris-service/src/main/java/org/apache/polaris/service/storage/PolarisStorageIntegrationProviderImpl.java
b/polaris-service/src/main/java/org/apache/polaris/service/storage/PolarisStorageIntegrationProviderImpl.java
index 3fb03413..579ef033 100644
---
a/polaris-service/src/main/java/org/apache/polaris/service/storage/PolarisStorageIntegrationProviderImpl.java
+++
b/polaris-service/src/main/java/org/apache/polaris/service/storage/PolarisStorageIntegrationProviderImpl.java
@@ -22,7 +22,6 @@ import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.auth.http.HttpTransportFactory;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.cloud.ServiceOptions;
-import java.io.IOException;
import java.util.EnumMap;
import java.util.Map;
import java.util.Set;
@@ -43,9 +42,12 @@ import software.amazon.awssdk.services.sts.StsClient;
public class PolarisStorageIntegrationProviderImpl implements
PolarisStorageIntegrationProvider {
private final Supplier<StsClient> stsClientSupplier;
+ private final Supplier<GoogleCredentials> gcpCredsProvider;
- public PolarisStorageIntegrationProviderImpl(Supplier<StsClient>
stsClientSupplier) {
+ public PolarisStorageIntegrationProviderImpl(
+ Supplier<StsClient> stsClientSupplier, Supplier<GoogleCredentials>
gcpCredsProvider) {
this.stsClientSupplier = stsClientSupplier;
+ this.gcpCredsProvider = gcpCredsProvider;
}
@Override
@@ -64,17 +66,12 @@ public class PolarisStorageIntegrationProviderImpl
implements PolarisStorageInte
new AwsCredentialsStorageIntegration(stsClientSupplier.get());
break;
case GCS:
- try {
- storageIntegration =
- (PolarisStorageIntegration<T>)
- new GcpCredentialsStorageIntegration(
- GoogleCredentials.getApplicationDefault(),
- ServiceOptions.getFromServiceLoader(
- HttpTransportFactory.class, NetHttpTransport::new));
- } catch (IOException e) {
- throw new RuntimeException(
- "Error initializing default google credentials. " +
e.getMessage());
- }
+ storageIntegration =
+ (PolarisStorageIntegration<T>)
+ new GcpCredentialsStorageIntegration(
+ gcpCredsProvider.get(),
+ ServiceOptions.getFromServiceLoader(
+ HttpTransportFactory.class, NetHttpTransport::new));
break;
case AZURE:
storageIntegration =
diff --git
a/polaris-service/src/test/java/org/apache/polaris/service/admin/PolarisAuthzTestBase.java
b/polaris-service/src/test/java/org/apache/polaris/service/admin/PolarisAuthzTestBase.java
index 702acf41..88b19303 100644
---
a/polaris-service/src/test/java/org/apache/polaris/service/admin/PolarisAuthzTestBase.java
+++
b/polaris-service/src/test/java/org/apache/polaris/service/admin/PolarisAuthzTestBase.java
@@ -20,9 +20,12 @@ package org.apache.polaris.service.admin;
import static org.apache.iceberg.types.Types.NestedField.required;
+import com.google.auth.oauth2.AccessToken;
+import com.google.auth.oauth2.GoogleCredentials;
import com.google.common.collect.ImmutableMap;
import java.io.IOException;
import java.time.Clock;
+import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -148,7 +151,8 @@ public abstract class PolarisAuthzTestBase {
InMemoryPolarisMetaStoreManagerFactory managerFactory =
new InMemoryPolarisMetaStoreManagerFactory();
managerFactory.setStorageIntegrationProvider(
- new PolarisStorageIntegrationProviderImpl(Mockito::mock));
+ new PolarisStorageIntegrationProviderImpl(
+ Mockito::mock, () -> GoogleCredentials.create(new
AccessToken("abc", new Date()))));
RealmContext realmContext = () -> "realm";
PolarisMetaStoreManager metaStoreManager =
managerFactory.getOrCreateMetaStoreManager(realmContext);
diff --git
a/polaris-service/src/test/java/org/apache/polaris/service/admin/PolarisServiceImplIntegrationTest.java
b/polaris-service/src/test/java/org/apache/polaris/service/admin/PolarisServiceImplIntegrationTest.java
index dbe95622..2eb5a452 100644
---
a/polaris-service/src/test/java/org/apache/polaris/service/admin/PolarisServiceImplIntegrationTest.java
+++
b/polaris-service/src/test/java/org/apache/polaris/service/admin/PolarisServiceImplIntegrationTest.java
@@ -59,6 +59,7 @@ import
org.apache.polaris.core.admin.model.CreatePrincipalRequest;
import org.apache.polaris.core.admin.model.CreatePrincipalRoleRequest;
import org.apache.polaris.core.admin.model.ExternalCatalog;
import org.apache.polaris.core.admin.model.FileStorageConfigInfo;
+import org.apache.polaris.core.admin.model.GcpStorageConfigInfo;
import org.apache.polaris.core.admin.model.GrantCatalogRoleRequest;
import org.apache.polaris.core.admin.model.GrantPrincipalRoleRequest;
import org.apache.polaris.core.admin.model.GrantResource;
@@ -107,7 +108,9 @@ public class PolarisServiceImplIntegrationTest {
// disallow FILE urls for the sake of tests below
ConfigOverride.config(
- "featureConfiguration.SUPPORTED_CATALOG_STORAGE_TYPES",
"S3,GCS,AZURE"));
+ "featureConfiguration.SUPPORTED_CATALOG_STORAGE_TYPES",
"S3,GCS,AZURE"),
+ ConfigOverride.config("gcp_credentials.access_token", "abc"),
+ ConfigOverride.config("gcp_credentials.expires_in", "12345"));
private static String userToken;
private static String realm;
@@ -379,6 +382,73 @@ public class PolarisServiceImplIntegrationTest {
}
}
+ @Test
+ public void testCreateCatalogWithAzureStorageConfig() {
+ AzureStorageConfigInfo azureConfigInfo =
+ AzureStorageConfigInfo.builder()
+ .setConsentUrl("https://consent.url")
+ .setMultiTenantAppName("myappname")
+ .setTenantId("tenantId")
+ .setStorageType(StorageConfigInfo.StorageTypeEnum.AZURE)
+ .build();
+ Catalog catalog =
+ PolarisCatalog.builder()
+ .setType(Catalog.TypeEnum.INTERNAL)
+ .setName("my-catalog")
+ .setProperties(
+ new CatalogProperties(
+
"abfss://[email protected]/path/to/my/data/"))
+ .setStorageConfigInfo(azureConfigInfo)
+ .build();
+ try (Response response =
+ newRequest("http://localhost:%d/api/management/v1/catalogs")
+ .post(Entity.json(new CreateCatalogRequest(catalog)))) {
+ assertThat(response).returns(Response.Status.CREATED.getStatusCode(),
Response::getStatus);
+ }
+ try (Response response =
+
newRequest("http://localhost:%d/api/management/v1/catalogs/my-catalog").get()) {
+ assertThat(response).returns(Response.Status.OK.getStatusCode(),
Response::getStatus);
+ Catalog catResponse = response.readEntity(Catalog.class);
+ assertThat(catResponse.getStorageConfigInfo())
+ .isInstanceOf(AzureStorageConfigInfo.class)
+ .hasFieldOrPropertyWithValue("consentUrl", "https://consent.url")
+ .hasFieldOrPropertyWithValue("multiTenantAppName", "myappname")
+ .hasFieldOrPropertyWithValue(
+ "allowedLocations",
+
List.of("abfss://[email protected]/path/to/my/data/"));
+ }
+ }
+
+ @Test
+ public void testCreateCatalogWithGcpStorageConfig() {
+ GcpStorageConfigInfo gcpConfigModel =
+ GcpStorageConfigInfo.builder()
+ .setGcsServiceAccount("my-sa")
+ .setStorageType(StorageConfigInfo.StorageTypeEnum.GCS)
+ .build();
+ Catalog catalog =
+ PolarisCatalog.builder()
+ .setType(Catalog.TypeEnum.INTERNAL)
+ .setName("my-catalog")
+ .setProperties(new
CatalogProperties("gs://my-bucket/path/to/data"))
+ .setStorageConfigInfo(gcpConfigModel)
+ .build();
+ try (Response response =
+ newRequest("http://localhost:%d/api/management/v1/catalogs")
+ .post(Entity.json(new CreateCatalogRequest(catalog)))) {
+ assertThat(response).returns(Response.Status.CREATED.getStatusCode(),
Response::getStatus);
+ }
+ try (Response response =
+
newRequest("http://localhost:%d/api/management/v1/catalogs/my-catalog").get()) {
+ assertThat(response).returns(Response.Status.OK.getStatusCode(),
Response::getStatus);
+ Catalog catResponse = response.readEntity(Catalog.class);
+ assertThat(catResponse.getStorageConfigInfo())
+ .isInstanceOf(GcpStorageConfigInfo.class)
+ .hasFieldOrPropertyWithValue("gcsServiceAccount", "my-sa")
+ .hasFieldOrPropertyWithValue("allowedLocations",
List.of("gs://my-bucket/path/to/data"));
+ }
+ }
+
@Test
public void testCreateCatalogWithNullBaseLocation() {
AwsStorageConfigInfo awsConfigModel =
diff --git
a/polaris-service/src/test/java/org/apache/polaris/service/catalog/BasePolarisCatalogViewTest.java
b/polaris-service/src/test/java/org/apache/polaris/service/catalog/BasePolarisCatalogViewTest.java
index d64ac3bb..4f6a2753 100644
---
a/polaris-service/src/test/java/org/apache/polaris/service/catalog/BasePolarisCatalogViewTest.java
+++
b/polaris-service/src/test/java/org/apache/polaris/service/catalog/BasePolarisCatalogViewTest.java
@@ -18,8 +18,11 @@
*/
package org.apache.polaris.service.catalog;
+import com.google.auth.oauth2.AccessToken;
+import com.google.auth.oauth2.GoogleCredentials;
import com.google.common.collect.ImmutableMap;
import java.time.Clock;
+import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -66,7 +69,8 @@ public class BasePolarisCatalogViewTest extends
ViewCatalogTests<BasePolarisCata
InMemoryPolarisMetaStoreManagerFactory managerFactory =
new InMemoryPolarisMetaStoreManagerFactory();
managerFactory.setStorageIntegrationProvider(
- new PolarisStorageIntegrationProviderImpl(Mockito::mock));
+ new PolarisStorageIntegrationProviderImpl(
+ Mockito::mock, () -> GoogleCredentials.create(new
AccessToken("abc", new Date()))));
PolarisMetaStoreManager metaStoreManager =
managerFactory.getOrCreateMetaStoreManager(realmContext);
Map<String, Object> configMap = new HashMap<>();