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

uros-b pushed a commit to branch branch-4.x
in repository https://gitbox.apache.org/repos/asf/spark.git


The following commit(s) were added to refs/heads/branch-4.x by this push:
     new 8afa4418ad16 [SPARK-59966][SQL] Only add explicitly configured remote 
repositories with custom Ivy settings
8afa4418ad16 is described below

commit 8afa4418ad16162a310d1d3d81ad66a57790b95e
Author: qianlan717 <[email protected]>
AuthorDate: Tue Oct 6 10:41:51 2026 +0000

    [SPARK-59966][SQL] Only add explicitly configured remote repositories with 
custom Ivy settings
    
    ### What changes were proposed in this pull request?
    
    This PR changes Hive metastore dependency resolution when a custom
    `spark.jars.ivySettings` file is configured:
    
    - Add `spark.sql.maven.additionalRemoteRepositories` only when the user 
explicitly sets it.
    - Preserve the existing effective default repository behavior when no 
custom Ivy settings file
      is used.
    - Document the custom Ivy settings behavior.
    - Add regression coverage for default and explicitly configured 
repositories.
    
    ### Why are the changes needed?
    
    `spark.sql.maven.additionalRemoteRepositories` has a default Google Cloud 
Storage Maven mirror.
    `IsolatedClientLoader` previously read the effective value unconditionally 
and appended that
    mirror even when users supplied a custom Ivy settings file.
    
    Users who provide custom Ivy settings expect those settings to control 
repository resolution.
    Implicitly adding another repository can violate network or repository 
policy and cause
    unexpected outbound access.
    
    ### Does this PR introduce _any_ user-facing change?
    
    Yes.
    
    Previously, setting `spark.jars.ivySettings` implicitly appended the default
    `spark.sql.maven.additionalRemoteRepositories` value, including the default 
Google Cloud Storage
    mirror.
    
    After this change, a custom Ivy settings file is used without additional 
remote repositories
    unless `spark.sql.maven.additionalRemoteRepositories` is explicitly 
configured. Behavior without
    a custom Ivy settings file remains unchanged.
    
    ### How was this patch tested?
    
    Added assertions to `IsolatedClientLoaderIvySettingsSuite` covering:
    
    - Custom Ivy settings with no explicit additional repositories.
    - Resolution without custom Ivy settings, which retains the effective 
default.
    - Explicit additional repositories with and without custom Ivy settings.
    
    Ran:
    
    ```bash
    build/sbt 'hive/testOnly 
org.apache.spark.sql.hive.client.IsolatedClientLoaderIvySettingsSuite'
    ```
    
    Result: 2 tests passed, 0 failed.
    
    ### Was this patch authored or co-authored using generative AI tooling?
    
    Generated-by: OpenAI Codex (GPT-5)
    
    Closes #59220 from 
qianlan717/qianlan-chen_data/hive-ivy-settings-remote-repos.
    
    Authored-by: qianlan717 <[email protected]>
    Signed-off-by: Uros Bojanic <[email protected]>
    (cherry picked from commit 7d30a7e0e047040b4bf2ebd864fe23bdfd9a1de2)
    Signed-off-by: Uros Bojanic <[email protected]>
---
 .../org/apache/spark/sql/internal/SQLConf.scala    |  3 ++-
 .../sql/hive/client/IsolatedClientLoader.scala     | 23 ++++++++++++++-----
 .../IsolatedClientLoaderIvySettingsSuite.scala     | 26 ++++++++++++++++++++++
 3 files changed, 45 insertions(+), 7 deletions(-)

diff --git 
a/sql/catalyst/src/main/scala/org/apache/spark/sql/internal/SQLConf.scala 
b/sql/catalyst/src/main/scala/org/apache/spark/sql/internal/SQLConf.scala
index ce88b88bdf4c..5ec5479683b0 100644
--- a/sql/catalyst/src/main/scala/org/apache/spark/sql/internal/SQLConf.scala
+++ b/sql/catalyst/src/main/scala/org/apache/spark/sql/internal/SQLConf.scala
@@ -6913,7 +6913,8 @@ object SQLConf {
     buildConf("spark.sql.maven.additionalRemoteRepositories")
       .doc("A comma-delimited string config of the optional additional remote 
Maven mirror " +
         "repositories. This is only used for downloading Hive jars in 
IsolatedClientLoader " +
-        "if the default Maven Central repo is unreachable.")
+        "if the default Maven Central repo is unreachable. When 
spark.jars.ivySettings is " +
+        "set, the repositories are added only if this configuration is 
explicitly set.")
       .version("3.0.0")
       .stringConf
       .createWithDefault(
diff --git 
a/sql/hive/src/main/scala/org/apache/spark/sql/hive/client/IsolatedClientLoader.scala
 
b/sql/hive/src/main/scala/org/apache/spark/sql/hive/client/IsolatedClientLoader.scala
index 950f7bc752a8..fcd3c5834f82 100644
--- 
a/sql/hive/src/main/scala/org/apache/spark/sql/hive/client/IsolatedClientLoader.scala
+++ 
b/sql/hive/src/main/scala/org/apache/spark/sql/hive/client/IsolatedClientLoader.scala
@@ -57,8 +57,8 @@ private[hive] object IsolatedClientLoader extends Logging {
     val files = if (resolvedVersions.contains((resolvedVersion, 
hadoopVersion))) {
       resolvedVersions((resolvedVersion, hadoopVersion))
     } else {
-      val remoteRepos = sparkConf.get(SQLConf.ADDITIONAL_REMOTE_REPOSITORIES)
       val ivySettingsPath = 
sparkConf.getOption(MavenUtils.JAR_IVY_SETTING_PATH_KEY)
+      val remoteRepos = getRemoteRepos(sparkConf, ivySettingsPath)
       val (downloadedFiles, actualHadoopVersion) =
         try {
           (downloadVersion(resolvedVersion, hadoopVersion, ivyPath, 
ivySettingsPath, remoteRepos),
@@ -121,12 +121,23 @@ private[hive] object IsolatedClientLoader extends Logging 
{
     }
   }
 
+  private[hive] def getRemoteRepos(
+      sparkConf: SparkConf,
+      ivySettingsPath: Option[String]): Option[String] = {
+    if (ivySettingsPath.isEmpty ||
+        sparkConf.contains(SQLConf.ADDITIONAL_REMOTE_REPOSITORIES)) {
+      Some(sparkConf.get(SQLConf.ADDITIONAL_REMOTE_REPOSITORIES))
+    } else {
+      None
+    }
+  }
+
   private def downloadVersion(
       version: HiveVersion,
       hadoopVersion: String,
       ivyPath: Option[String],
       ivySettingsPath: Option[String],
-      remoteRepos: String): Seq[URL] = {
+      remoteRepos: Option[String]): Seq[URL] = {
     val hadoopJarNames = if (supportsHadoopShadedClient(hadoopVersion)) {
       Seq(s"org.apache.hadoop:hadoop-client-api:$hadoopVersion",
         s"org.apache.hadoop:hadoop-client-runtime:$hadoopVersion")
@@ -140,15 +151,15 @@ private[hive] object IsolatedClientLoader extends Logging 
{
     implicit val printStream: PrintStream = SparkSubmit.printStream
     val ivySettings = ivySettingsPath match {
       case Some(path) =>
-        MavenUtils.loadIvySettings(path, Some(remoteRepos), ivyPath)
+        MavenUtils.loadIvySettings(path, remoteRepos, ivyPath)
       case None =>
-        MavenUtils.buildIvySettings(Some(remoteRepos), ivyPath)
+        MavenUtils.buildIvySettings(remoteRepos, ivyPath)
     }
     val noCacheIvySettings = ivySettingsPath match {
       case Some(path) =>
-        Some(MavenUtils.loadIvySettings(path, Some(remoteRepos), ivyPath))
+        Some(MavenUtils.loadIvySettings(path, remoteRepos, ivyPath))
       case None =>
-        Some(MavenUtils.buildIvySettings(Some(remoteRepos), ivyPath, 
useLocalM2AsCache = false))
+        Some(MavenUtils.buildIvySettings(remoteRepos, ivyPath, 
useLocalM2AsCache = false))
     }
     val classpaths = quietly {
       MavenUtils.resolveMavenCoordinates(
diff --git 
a/sql/hive/src/test/scala/org/apache/spark/sql/hive/client/IsolatedClientLoaderIvySettingsSuite.scala
 
b/sql/hive/src/test/scala/org/apache/spark/sql/hive/client/IsolatedClientLoaderIvySettingsSuite.scala
index 265ced9065f4..72b1d413c8c2 100644
--- 
a/sql/hive/src/test/scala/org/apache/spark/sql/hive/client/IsolatedClientLoaderIvySettingsSuite.scala
+++ 
b/sql/hive/src/test/scala/org/apache/spark/sql/hive/client/IsolatedClientLoaderIvySettingsSuite.scala
@@ -23,11 +23,37 @@ import org.apache.hadoop.conf.Configuration
 import org.apache.hadoop.util.VersionInfo
 
 import org.apache.spark.{SparkConf, SparkFunSuite}
+import org.apache.spark.sql.internal.SQLConf
 import org.apache.spark.util.{MavenUtils, Utils}
 
 class IsolatedClientLoaderIvySettingsSuite extends SparkFunSuite {
   override protected val enableAutoThreadAudit = false
 
+  test("only add remote repositories explicitly configured with custom Ivy 
settings") {
+    val sparkConf = new SparkConf()
+    val ivySettingsPath = Some("ivysettings.xml")
+
+    assert(IsolatedClientLoader.getRemoteRepos(sparkConf, 
ivySettingsPath).isEmpty)
+    assert(IsolatedClientLoader.getRemoteRepos(sparkConf, None).contains(
+      sparkConf.get(SQLConf.ADDITIONAL_REMOTE_REPOSITORIES)))
+
+    val remoteRepos = "https://repository.example.com/maven2/";
+    sparkConf.set(SQLConf.ADDITIONAL_REMOTE_REPOSITORIES.key, remoteRepos)
+    assert(IsolatedClientLoader.getRemoteRepos(sparkConf, 
ivySettingsPath).contains(remoteRepos))
+    assert(IsolatedClientLoader.getRemoteRepos(sparkConf, 
None).contains(remoteRepos))
+  }
+
+  test("substitute variables in remote repositories with custom Ivy settings") 
{
+    val sparkConf = new SparkConf()
+    val remoteRepos = "https://repository.example.com/maven2/";
+    sparkConf.set("spark.test.remoteRepository", remoteRepos)
+    sparkConf.set(
+      SQLConf.ADDITIONAL_REMOTE_REPOSITORIES.key, 
"${spark.test.remoteRepository}")
+
+    assert(IsolatedClientLoader.getRemoteRepos(sparkConf, 
Some("ivysettings.xml"))
+      .contains(remoteRepos))
+  }
+
   test("SPARK-56867: respect spark.jars.ivySettings when downloading Hive 
metastore jars") {
     val ivyPath = Utils.createTempDir(namePrefix = "ivy-settings-test")
     val ivySettingsFile = new File(ivyPath, "ivysettings.xml")


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to