This is an automated email from the ASF dual-hosted git repository.

liuxun pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/main by this push:
     new 8cda4d4a2 [#5648] improvement(iceberg): generate credential according 
to the data path and metadata path (#5698)
8cda4d4a2 is described below

commit 8cda4d4a2d1b8a5b6e8fa1347e1f5c593d524e26
Author: JUN <oren....@gmail.com>
AuthorDate: Thu Nov 28 20:43:01 2024 +0800

    [#5648] improvement(iceberg): generate credential according to the data 
path and metadata path (#5698)
    
    ### What changes were proposed in this pull request?
    
    This PR updates the credential generation process to also consider
    `write.data.path` and `write.metadata.path`.
    
    ### Why are the changes needed?
    
    To provide greater flexibility for users.
    Fixes: #5648
    
    ### Does this PR introduce _any_ user-facing change?
    
    No.
    
    ### How was this patch tested?
    
    - Used Spark to create a table via Gravitino Iceberg REST server to AWS
    S3 and verified that `write.data.path` and `write.metadata.path` are
    working as expected.
    - Added unit tests to check if table properties include
    `write.data.path` and `write.metadata.path`.
    
    
![image](https://github.com/user-attachments/assets/fc2ebbfc-e758-4800-99ce-a575bb23605c)
---
 .../gravitino/credential/CredentialUtils.java      |  4 +-
 .../service/rest/IcebergTableOperations.java       | 17 ++++-
 .../iceberg/integration/test/IcebergRESTS3IT.java  | 72 ++++++++++++++++++++++
 .../integration/test/IcebergRESTServiceIT.java     |  2 +-
 4 files changed, 89 insertions(+), 6 deletions(-)

diff --git 
a/core/src/main/java/org/apache/gravitino/credential/CredentialUtils.java 
b/core/src/main/java/org/apache/gravitino/credential/CredentialUtils.java
index ad81953ac..09439d58a 100644
--- a/core/src/main/java/org/apache/gravitino/credential/CredentialUtils.java
+++ b/core/src/main/java/org/apache/gravitino/credential/CredentialUtils.java
@@ -23,10 +23,10 @@ import com.google.common.collect.ImmutableSet;
 import org.apache.gravitino.utils.PrincipalUtils;
 
 public class CredentialUtils {
-  public static Credential vendCredential(CredentialProvider 
credentialProvider, String path) {
+  public static Credential vendCredential(CredentialProvider 
credentialProvider, String[] path) {
     PathBasedCredentialContext pathBasedCredentialContext =
         new PathBasedCredentialContext(
-            PrincipalUtils.getCurrentUserName(), ImmutableSet.of(path), 
ImmutableSet.of());
+            PrincipalUtils.getCurrentUserName(), ImmutableSet.copyOf(path), 
ImmutableSet.of());
     return credentialProvider.getCredential(pathBasedCredentialContext);
   }
 }
diff --git 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergTableOperations.java
 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergTableOperations.java
index 67dcfce02..12f9c5055 100644
--- 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergTableOperations.java
+++ 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergTableOperations.java
@@ -24,6 +24,7 @@ import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.google.common.annotations.VisibleForTesting;
 import java.util.Map;
+import java.util.stream.Stream;
 import javax.inject.Inject;
 import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.Consumes;
@@ -54,6 +55,8 @@ import 
org.apache.gravitino.iceberg.service.dispatcher.IcebergTableOperationDisp
 import org.apache.gravitino.iceberg.service.metrics.IcebergMetricsManager;
 import org.apache.gravitino.listener.api.event.IcebergRequestContext;
 import org.apache.gravitino.metrics.MetricNames;
+import org.apache.iceberg.TableMetadata;
+import org.apache.iceberg.TableProperties;
 import org.apache.iceberg.catalog.Namespace;
 import org.apache.iceberg.catalog.TableIdentifier;
 import org.apache.iceberg.exceptions.ServiceUnavailableException;
@@ -294,9 +297,17 @@ public class IcebergTableOperations {
               + CredentialConstants.CREDENTIAL_PROVIDER_TYPE
               + " to the catalog configurations");
     }
-    Credential credential =
-        CredentialUtils.vendCredential(
-            credentialProvider, loadTableResponse.tableMetadata().location());
+
+    TableMetadata tableMetadata = loadTableResponse.tableMetadata();
+    String[] path =
+        Stream.of(
+                tableMetadata.location(),
+                tableMetadata.property(TableProperties.WRITE_DATA_LOCATION, 
""),
+                
tableMetadata.property(TableProperties.WRITE_METADATA_LOCATION, ""))
+            .filter(StringUtils::isNotBlank)
+            .toArray(String[]::new);
+
+    Credential credential = CredentialUtils.vendCredential(credentialProvider, 
path);
     if (credential == null) {
       throw new ServiceUnavailableException(
           "Couldn't generate credential for %s", 
credentialProvider.credentialType());
diff --git 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/integration/test/IcebergRESTS3IT.java
 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/integration/test/IcebergRESTS3IT.java
index 7941177a7..ab372f78c 100644
--- 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/integration/test/IcebergRESTS3IT.java
+++ 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/integration/test/IcebergRESTS3IT.java
@@ -19,8 +19,10 @@
 
 package org.apache.gravitino.iceberg.integration.test;
 
+import com.google.common.collect.ImmutableList;
 import java.io.IOException;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import org.apache.gravitino.catalog.lakehouse.iceberg.IcebergConstants;
@@ -30,9 +32,13 @@ import org.apache.gravitino.integration.test.util.BaseIT;
 import org.apache.gravitino.integration.test.util.DownloaderUtils;
 import org.apache.gravitino.integration.test.util.ITUtils;
 import org.apache.gravitino.storage.S3Properties;
+import org.apache.iceberg.TableProperties;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
 import org.junit.platform.commons.util.StringUtils;
 
+@SuppressWarnings("FormatStringAnnotation")
 @EnabledIfEnvironmentVariable(named = "GRAVITINO_TEST_CLOUD_IT", matches = 
"true")
 public class IcebergRESTS3IT extends IcebergRESTJdbcCatalogIT {
 
@@ -124,4 +130,70 @@ public class IcebergRESTS3IT extends 
IcebergRESTJdbcCatalogIT {
     String envValue = System.getenv(envVar);
     return Optional.ofNullable(envValue).orElse(defaultValue);
   }
+
+  /**
+   * Parses a string representing table properties into a map of key-value 
pairs.
+   *
+   * @param tableProperties A string representing the table properties in the 
format:
+   *     "[key1=value1,key2=value2,...]"
+   * @return A Map where each key is a property name (String) and the 
corresponding value is the
+   *     property value (String). Example input:
+   *     "[write.data.path=path/to/data,write.metadata.path=path/to/metadata]" 
Example output: {
+   *     "write.data.path" -> "path/to/data", "write.metadata.path" -> 
"path/to/metadata" }
+   */
+  private Map<String, String> parseTableProperties(String tableProperties) {
+    Map<String, String> propertiesMap = new HashMap<>();
+    String[] pairs = tableProperties.substring(1, tableProperties.length() - 
1).split(",");
+    for (String pair : pairs) {
+      String[] keyValue = pair.split("=", 2); // Split at most once
+      if (keyValue.length == 2) {
+        propertiesMap.put(keyValue[0].trim(), keyValue[1].trim());
+      }
+    }
+    return propertiesMap;
+  }
+
+  @Test
+  void testCredentialWithMultiLocations() {
+    String namespaceName = ICEBERG_REST_NS_PREFIX + "credential";
+    String tableName = namespaceName + ".multi_location";
+
+    String writeDataPath = this.s3Warehouse + "/test_data_location";
+    String writeMetaDataPath = this.s3Warehouse + "/test_metadata_location";
+
+    sql("CREATE DATABASE IF NOT EXISTS " + namespaceName);
+    sql(
+        String.format(
+            "CREATE TABLE %s (id bigint) USING iceberg OPTIONS ('%s' = '%s', 
'%s' = '%s')",
+            tableName,
+            TableProperties.WRITE_DATA_LOCATION,
+            writeDataPath,
+            TableProperties.WRITE_METADATA_LOCATION,
+            writeMetaDataPath));
+
+    Map<String, String> tableDetails =
+        convertToStringMap(sql("DESCRIBE TABLE EXTENDED " + tableName));
+    String tableProperties = tableDetails.get("Table Properties");
+    Assertions.assertNotNull(tableProperties, "Table Properties should not be 
null");
+
+    Map<String, String> propertiesMap = parseTableProperties(tableProperties);
+    Assertions.assertEquals(
+        writeDataPath,
+        propertiesMap.get(TableProperties.WRITE_DATA_LOCATION),
+        String.format(
+            "Expected write.data.path to be '%s', but was '%s'",
+            writeDataPath, 
propertiesMap.get(TableProperties.WRITE_DATA_LOCATION)));
+    Assertions.assertEquals(
+        writeMetaDataPath,
+        propertiesMap.get(TableProperties.WRITE_METADATA_LOCATION),
+        String.format(
+            "Expected write.metadata.path to be '%s', but was '%s'",
+            writeMetaDataPath, 
propertiesMap.get(TableProperties.WRITE_METADATA_LOCATION)));
+
+    String value1 = "1";
+    String value2 = "2";
+    sql(String.format("INSERT INTO %s VALUES (%s), (%s);", tableName, value1, 
value2));
+    List<String> result = convertToStringList(sql(String.format("SELECT * FROM 
%s", tableName)), 0);
+    Assertions.assertEquals(result, ImmutableList.of((value1), (value2)));
+  }
 }
diff --git 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/integration/test/IcebergRESTServiceIT.java
 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/integration/test/IcebergRESTServiceIT.java
index 9b4900f4d..f8bd016a9 100644
--- 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/integration/test/IcebergRESTServiceIT.java
+++ 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/integration/test/IcebergRESTServiceIT.java
@@ -45,7 +45,7 @@ import org.junit.jupiter.api.condition.EnabledIf;
 @TestInstance(Lifecycle.PER_CLASS)
 public abstract class IcebergRESTServiceIT extends IcebergRESTServiceBaseIT {
 
-  private static final String ICEBERG_REST_NS_PREFIX = "iceberg_rest_";
+  protected static final String ICEBERG_REST_NS_PREFIX = "iceberg_rest_";
 
   @BeforeAll
   void prepareSQLContext() {

Reply via email to