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

roryqi 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 06d0b439e4 [#12427] test(iceberg): fix cloud credential vending ITs in 
deploy mode (#12428)
06d0b439e4 is described below

commit 06d0b439e4fbe3bb64cc3c54c68505b5799b3c03
Author: Bharath Krishna <[email protected]>
AuthorDate: Wed Aug 12 18:44:04 2026 -0700

    [#12427] test(iceberg): fix cloud credential vending ITs in deploy mode 
(#12428)
    
    ### What changes were proposed in this pull request?
    
    Makes the cloud credential vending ITs work in deploy mode, and tidies
    up the base class they share.
    
    - `setupCloudBundles()` now runs before `super.startIntegrationTest()`,
    because the server resolves its `FileIO` from the classpath while
    starting.
    - Bundles are installed into `catalogs/lakehouse-iceberg/libs/` as well
    as `iceberg-rest-server/libs/`. The catalog is loaded through
    `IsolatedClassLoader` and ships `iceberg-aws` / `iceberg-gcp` without
    the cloud SDK, so `FileIO` resolved and then failed with
    `NoClassDefFoundError`. Both bundles now come from the local build, so
    nothing is downloaded from Maven Central.
    - `downloadCloudBundleJar()` is removed: every subclass implemented it
    as a no-op once the jars came from the local build.
    - The schema create/drop lifecycle moves into the base class, which
    already owns `SCHEMA_NAME` and `clearTable()`. The tests no longer leak
    `schema` into sibling Iceberg ITs.
    
    GCS needs two jars per directory, since `bundles/iceberg-gcp-bundle`
    does not shade `bundles/gcp`; the AWS bundle already shades
    `bundles/aws`, so one is enough there.
    
    ### Why are the changes needed?
    
    The S3 and GCS ITs fail immediately under `-PtestMode=deploy`. They pass
    in embedded mode, where `setupCloudBundles()` returns early and the
    bundle code never runs, which is why this went unnoticed.
    
    Fix: #12427
    
    ### Does this PR introduce _any_ user-facing change?
    
    No. Test only.
    
    ### How was this patch tested?
    
    GCS against a real bucket, and MinIO, both in deploy mode:
    
    ```
    ./gradlew :iceberg:iceberg-rest-server:test --tests 
"*IcebergRESTGCSTokenAuthorizationIT" \
      -PskipTests -PtestMode=deploy -PskipDockerTests=false
    ```
    
    | Test | Before | After |
    | --- | --- | --- |
    | GCS deploy | `ServiceFailureException` at `initAdminIcebergCatalog` |
    3 pass |
    | GCS embedded | 3 pass | 3 pass |
    | MinIO deploy | 3 pass | 3 pass |
    
    MinIO is covered here because it shares the base class and runs in CI.
    
    The S3 change is identical in shape but unverified, since running that
    test needs an AWS account with an assumable role. S3 and GCS both stay
    gated behind `GRAVITINO_TEST_CLOUD_IT`, so neither runs in CI.
---
 .../IcebergRESTCloudTokenAuthorizationBaseIT.java  | 39 +++++++++++++---------
 .../test/IcebergRESTGCSTokenAuthorizationIT.java   | 30 +++++++----------
 .../test/IcebergRESTMinIOTokenAuthorizationIT.java | 28 ++--------------
 .../test/IcebergRESTS3TokenAuthorizationIT.java    | 29 ++++++----------
 4 files changed, 47 insertions(+), 79 deletions(-)

diff --git 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/integration/test/IcebergRESTCloudTokenAuthorizationBaseIT.java
 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/integration/test/IcebergRESTCloudTokenAuthorizationBaseIT.java
index 83b14fc70d..583be10d55 100644
--- 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/integration/test/IcebergRESTCloudTokenAuthorizationBaseIT.java
+++ 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/integration/test/IcebergRESTCloudTokenAuthorizationBaseIT.java
@@ -20,6 +20,7 @@
 package org.apache.gravitino.iceberg.integration.test;
 
 import com.google.common.collect.ImmutableList;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -37,6 +38,7 @@ import org.apache.gravitino.authorization.SecurableObjects;
 import org.apache.gravitino.integration.test.util.ITUtils;
 import org.apache.iceberg.exceptions.ForbiddenException;
 import org.apache.spark.SparkException;
+import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -82,14 +84,6 @@ public abstract class 
IcebergRESTCloudTokenAuthorizationBaseIT extends IcebergAu
   @Override
   public abstract Map<String, String> getCustomProperties();
 
-  /**
-   * Downloads cloud-specific bundle JARs (e.g., iceberg-aws-bundle, 
iceberg-gcp-bundle). Subclasses
-   * implement this to download the appropriate bundle for their cloud 
provider.
-   *
-   * @throws Exception if download fails
-   */
-  protected abstract void downloadCloudBundleJar() throws Exception;
-
   /**
    * Copies cloud-specific bundle JARs to the Iceberg REST server libs 
directory. Subclasses
    * implement this to copy the appropriate bundle (e.g., "aws", "gcp", 
"azure").
@@ -105,10 +99,9 @@ public abstract class 
IcebergRESTCloudTokenAuthorizationBaseIT extends IcebergAu
   protected abstract String getCloudProviderName();
 
   /**
-   * Sets up cloud-specific bundle JARs by downloading and copying them. This 
method should be
-   * called from subclass {@code startIntegrationTest()} methods before 
calling {@code
-   * super.startIntegrationTest()}, because the server resolves the cloud 
{@code FileIO} from its
-   * classpath while starting.
+   * Copies the cloud-specific bundle JARs into place. This method should be 
called from subclass
+   * {@code startIntegrationTest()} methods before calling {@code 
super.startIntegrationTest()},
+   * because the server resolves the cloud {@code FileIO} from its classpath 
while starting.
    *
    * <p>Skips setup if running in embedded mode.
    */
@@ -116,12 +109,26 @@ public abstract class 
IcebergRESTCloudTokenAuthorizationBaseIT extends IcebergAu
     if (ITUtils.isEmbedded()) {
       return;
     }
+    copyCloudBundleJar();
+  }
+
+  /** Subclasses call this after {@code super.startIntegrationTest()}. */
+  protected void createSchemaIfAbsent() {
+    if (!catalogClientWithAllPrivilege.asSchemas().schemaExists(SCHEMA_NAME)) {
+      catalogClientWithAllPrivilege.asSchemas().createSchema(SCHEMA_NAME, 
"test", new HashMap<>());
+    }
+  }
+
+  @AfterAll
+  public void stopIntegrationTest() throws IOException, InterruptedException {
+    // super drops the metalake, so it has to run even when setup failed part 
way through.
     try {
-      downloadCloudBundleJar();
-    } catch (Exception e) {
-      throw new RuntimeException("Failed to download cloud bundle JAR", e);
+      // The Iceberg JDBC backend is shared with sibling ITs; Iceberg has no 
cascading drop.
+      clearTable();
+      catalogClientWithAllPrivilege.asSchemas().dropSchema(SCHEMA_NAME, false);
+    } finally {
+      super.stopIntegrationTest();
     }
-    copyCloudBundleJar();
   }
 
   @Test
diff --git 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/integration/test/IcebergRESTGCSTokenAuthorizationIT.java
 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/integration/test/IcebergRESTGCSTokenAuthorizationIT.java
index b630072636..b341c826aa 100644
--- 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/integration/test/IcebergRESTGCSTokenAuthorizationIT.java
+++ 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/integration/test/IcebergRESTGCSTokenAuthorizationIT.java
@@ -19,14 +19,12 @@
 
 package org.apache.gravitino.iceberg.integration.test;
 
-import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
 import org.apache.gravitino.catalog.lakehouse.iceberg.IcebergConstants;
 import org.apache.gravitino.credential.CredentialConstants;
 import org.apache.gravitino.credential.GCSTokenCredential;
 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.GCSProperties;
 import org.junit.jupiter.api.BeforeAll;
@@ -63,11 +61,12 @@ public class IcebergRESTGCSTokenAuthorizationIT extends 
IcebergRESTCloudTokenAut
     // Use null to trigger ADC (Application Default Credentials) if not 
explicitly provided
     this.serviceAccountFile = 
System.getenv().get("GOOGLE_APPLICATION_CREDENTIALS");
 
-    super.startIntegrationTest();
+    // The server resolves GCSFileIO from its classpath at startup, so bundles 
must land first.
+    setupCloudBundles();
 
-    catalogClientWithAllPrivilege.asSchemas().createSchema(SCHEMA_NAME, 
"test", new HashMap<>());
+    super.startIntegrationTest();
 
-    setupCloudBundles();
+    createSchemaIfAbsent();
   }
 
   @Override
@@ -83,22 +82,17 @@ public class IcebergRESTGCSTokenAuthorizationIT extends 
IcebergRESTCloudTokenAut
   }
 
   @Override
-  protected void downloadCloudBundleJar() throws IOException {
-    String icebergBundleJarUri =
-        String.format(
-            "https://repo1.maven.org/maven2/org/apache/iceberg/";
-                + "iceberg-gcp-bundle/%s/iceberg-gcp-bundle-%s.jar",
-            ITUtils.icebergVersion(), ITUtils.icebergVersion());
+  protected void copyCloudBundleJar() {
     String gravitinoHome = System.getenv("GRAVITINO_HOME");
-    String targetDir = String.format("%s/iceberg-rest-server/libs/", 
gravitinoHome);
-    DownloaderUtils.downloadFile(icebergBundleJarUri, targetDir);
+    // The REST server and the catalog use separate classloaders, so each 
needs its own copy.
+    copyBundlesTo(ITUtils.joinPath(gravitinoHome, "iceberg-rest-server", 
"libs"));
+    copyBundlesTo(ITUtils.joinPath(gravitinoHome, "catalogs", 
"lakehouse-iceberg", "libs"));
   }
 
-  @Override
-  protected void copyCloudBundleJar() {
-    String gravitinoHome = System.getenv("GRAVITINO_HOME");
-    String targetDir = String.format("%s/iceberg-rest-server/libs/", 
gravitinoHome);
-    BaseIT.copyBundleJarsToDirectory("gcp", targetDir);
+  private void copyBundlesTo(String directory) {
+    // iceberg-gcp-bundle carries GCSFileIO and the GCS SDK, gcp carries the 
credential providers.
+    BaseIT.copyBundleJarsToDirectory("iceberg-gcp-bundle", directory);
+    BaseIT.copyBundleJarsToDirectory("gcp", directory);
   }
 
   private Map<String, String> getGCSConfig() {
diff --git 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/integration/test/IcebergRESTMinIOTokenAuthorizationIT.java
 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/integration/test/IcebergRESTMinIOTokenAuthorizationIT.java
index fe9dd561c0..5b7fb88bcd 100644
--- 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/integration/test/IcebergRESTMinIOTokenAuthorizationIT.java
+++ 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/integration/test/IcebergRESTMinIOTokenAuthorizationIT.java
@@ -18,7 +18,6 @@
  */
 package org.apache.gravitino.iceberg.integration.test;
 
-import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
 import org.apache.gravitino.catalog.lakehouse.iceberg.IcebergConstants;
@@ -29,7 +28,6 @@ import 
org.apache.gravitino.integration.test.container.MinIOContainer;
 import org.apache.gravitino.integration.test.util.BaseIT;
 import org.apache.gravitino.integration.test.util.ITUtils;
 import org.apache.gravitino.storage.S3Properties;
-import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Tag;
 
@@ -69,22 +67,7 @@ public class IcebergRESTMinIOTokenAuthorizationIT extends 
IcebergRESTCloudTokenA
 
     super.startIntegrationTest();
 
-    // A sibling IT may have left this schema behind in the shared Iceberg 
JDBC backend.
-    if (!catalogClientWithAllPrivilege.asSchemas().schemaExists(SCHEMA_NAME)) {
-      catalogClientWithAllPrivilege.asSchemas().createSchema(SCHEMA_NAME, 
"test", new HashMap<>());
-    }
-  }
-
-  @AfterAll
-  public void stopIntegrationTest() throws IOException, InterruptedException {
-    // super drops the metalake, so it has to run even when setup failed part 
way through.
-    try {
-      // The Iceberg JDBC backend is shared with sibling ITs; Iceberg has no 
cascading drop.
-      clearTable();
-      catalogClientWithAllPrivilege.asSchemas().dropSchema(SCHEMA_NAME, false);
-    } finally {
-      super.stopIntegrationTest();
-    }
+    createSchemaIfAbsent();
   }
 
   @Override
@@ -110,17 +93,10 @@ public class IcebergRESTMinIOTokenAuthorizationIT extends 
IcebergRESTCloudTokenA
     return "minio";
   }
 
-  @Override
-  protected void downloadCloudBundleJar() {
-    // gravitino-iceberg-aws-bundle already shades the Iceberg AWS bundle and 
the Gravitino
-    // credential providers, so it is built locally and nothing has to be 
fetched.
-  }
-
   @Override
   protected void copyCloudBundleJar() {
     String gravitinoHome = System.getenv("GRAVITINO_HOME");
-    // The REST server and the lakehouse-iceberg catalog resolve S3FileIO 
through separate
-    // classloaders, so each needs its own copy.
+    // The REST server and the catalog use separate classloaders, so each 
needs its own copy.
     BaseIT.copyBundleJarsToDirectory(
         BUNDLE_NAME, ITUtils.joinPath(gravitinoHome, "iceberg-rest-server", 
"libs"));
     BaseIT.copyBundleJarsToDirectory(
diff --git 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/integration/test/IcebergRESTS3TokenAuthorizationIT.java
 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/integration/test/IcebergRESTS3TokenAuthorizationIT.java
index ac5c85729b..10fd8f15e4 100644
--- 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/integration/test/IcebergRESTS3TokenAuthorizationIT.java
+++ 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/integration/test/IcebergRESTS3TokenAuthorizationIT.java
@@ -19,14 +19,12 @@
 
 package org.apache.gravitino.iceberg.integration.test;
 
-import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
 import org.apache.gravitino.catalog.lakehouse.iceberg.IcebergConstants;
 import org.apache.gravitino.credential.CredentialConstants;
 import org.apache.gravitino.credential.S3TokenCredential;
 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.junit.jupiter.api.BeforeAll;
@@ -55,11 +53,12 @@ public class IcebergRESTS3TokenAuthorizationIT extends 
IcebergRESTCloudTokenAuth
     this.roleArn = System.getenv().getOrDefault("GRAVITINO_S3_ROLE_ARN", 
"{ROLE_ARN}");
     this.externalId = System.getenv().getOrDefault("GRAVITINO_S3_EXTERNAL_ID", 
"");
 
-    super.startIntegrationTest();
+    // The server resolves S3FileIO from its classpath at startup, so the 
bundle must land first.
+    setupCloudBundles();
 
-    catalogClientWithAllPrivilege.asSchemas().createSchema(SCHEMA_NAME, 
"test", new HashMap<>());
+    super.startIntegrationTest();
 
-    setupCloudBundles();
+    createSchemaIfAbsent();
   }
 
   @Override
@@ -74,23 +73,15 @@ public class IcebergRESTS3TokenAuthorizationIT extends 
IcebergRESTCloudTokenAuth
     return "s3";
   }
 
-  @Override
-  protected void downloadCloudBundleJar() throws IOException {
-    String icebergBundleJarUri =
-        String.format(
-            "https://repo1.maven.org/maven2/org/apache/iceberg/";
-                + "iceberg-aws-bundle/%s/iceberg-aws-bundle-%s.jar",
-            ITUtils.icebergVersion(), ITUtils.icebergVersion());
-    String gravitinoHome = System.getenv("GRAVITINO_HOME");
-    String targetDir = String.format("%s/iceberg-rest-server/libs/", 
gravitinoHome);
-    DownloaderUtils.downloadFile(icebergBundleJarUri, targetDir);
-  }
-
   @Override
   protected void copyCloudBundleJar() {
     String gravitinoHome = System.getenv("GRAVITINO_HOME");
-    String targetDir = String.format("%s/iceberg-rest-server/libs/", 
gravitinoHome);
-    BaseIT.copyBundleJarsToDirectory("aws", targetDir);
+    // The REST server and the catalog use separate classloaders, so each 
needs its own copy.
+    BaseIT.copyBundleJarsToDirectory(
+        "iceberg-aws-bundle", ITUtils.joinPath(gravitinoHome, 
"iceberg-rest-server", "libs"));
+    BaseIT.copyBundleJarsToDirectory(
+        "iceberg-aws-bundle",
+        ITUtils.joinPath(gravitinoHome, "catalogs", "lakehouse-iceberg", 
"libs"));
   }
 
   private Map<String, String> getS3Config() {

Reply via email to