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

dongjoon pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/spark.git


The following commit(s) were added to refs/heads/master by this push:
     new 2420fe91ce98 [SPARK-53167][DEPLOY] Spark launcher isRemote also 
respects properties file
2420fe91ce98 is described below

commit 2420fe91ce980a444ab51cfdaa9af3d540c72652
Author: Cheng Pan <cheng...@apache.org>
AuthorDate: Thu Aug 7 07:51:36 2025 -0700

    [SPARK-53167][DEPLOY] Spark launcher isRemote also respects properties file
    
    ### What changes were proposed in this pull request?
    
    This PR modifies the `SparkSubmitCommandBuilder` to use "effective config" 
to evaluate `isRemote` instead of the passed args only. This makes 
`spark.remote` configured in the properties file(`spark-defaults.conf` or other 
files specified by `--properties-file`) effective.
    
    ### Why are the changes needed?
    
    ```
    $ sbin/start-connect-server.sh
    $ cat > conf/spark-connect.conf <<EOF
    spark.reomte=sc://localhost:15002
    EOF
    $ bin/spark-shell --properties-file conf/spark-connect.conf
    ```
    
    ```
    25/08/07 13:35:56 ERROR Main: Failed to initialize Spark session.
    org.apache.spark.SparkException: A master URL must be set in your 
configuration
            at org.apache.spark.SparkContext.<init>(SparkContext.scala:421)
            at 
org.apache.spark.SparkContext$.getOrCreate(SparkContext.scala:3055)
            at 
org.apache.spark.sql.classic.SparkSession$Builder.$anonfun$build$2(SparkSession.scala:839)
            at scala.Option.getOrElse(Option.scala:201)
            at 
org.apache.spark.sql.classic.SparkSession$Builder.build(SparkSession.scala:830)
            at 
org.apache.spark.sql.classic.SparkSession$Builder.getOrCreate(SparkSession.scala:859)
            at 
org.apache.spark.sql.classic.SparkSession$Builder.getOrCreate(SparkSession.scala:732)
            at 
org.apache.spark.sql.SparkSession$Builder.getOrCreate(SparkSession.scala:923)
            at org.apache.spark.repl.Main$.createSparkSession(Main.scala:116)
    ```
    
    ### Does this PR introduce _any_ user-facing change?
    
    Yes, a kind of bug fix.
    
    ### How was this patch tested?
    
    UT is added, plus manually test:
    
    ```
    $ sbin/start-connect-server.sh
    $ cat > conf/spark-connect.conf <<EOF
    spark.reomte=sc://localhost:15002
    EOF
    $ bin/spark-shell --properties-file conf/spark-connect.conf
    ```
    The previously failed case now works.
    
    ### Was this patch authored or co-authored using generative AI tooling?
    
    No.
    
    Closes #51896 from pan3793/SPARK-53167.
    
    Authored-by: Cheng Pan <cheng...@apache.org>
    Signed-off-by: Dongjoon Hyun <dongj...@apache.org>
---
 .../spark/launcher/SparkSubmitCommandBuilder.java  | 18 ++++----
 .../launcher/SparkSubmitCommandBuilderSuite.java   | 49 +++++++++++++---------
 2 files changed, 40 insertions(+), 27 deletions(-)

diff --git 
a/launcher/src/main/java/org/apache/spark/launcher/SparkSubmitCommandBuilder.java
 
b/launcher/src/main/java/org/apache/spark/launcher/SparkSubmitCommandBuilder.java
index 5efa3bef78bc..bdbb954dbe08 100644
--- 
a/launcher/src/main/java/org/apache/spark/launcher/SparkSubmitCommandBuilder.java
+++ 
b/launcher/src/main/java/org/apache/spark/launcher/SparkSubmitCommandBuilder.java
@@ -172,6 +172,16 @@ class SparkSubmitCommandBuilder extends 
AbstractCommandBuilder {
   @Override
   public List<String> buildCommand(Map<String, String> env)
       throws IOException, IllegalArgumentException {
+    for (Map.Entry<String, String> entry : getEffectiveConfig().entrySet()) {
+      // If both spark.remote and spark.master are set, the error will be 
thrown later
+      // when the application is started.
+      if (entry.getKey().equals("spark.remote")) {
+        isRemote = true;
+      } else if (entry.getKey().equals(SparkLauncher.SPARK_API_MODE)) {
+        // Respects if the API mode is explicitly set.
+        isRemote = entry.getValue().equalsIgnoreCase("connect");
+      }
+    }
     if (PYSPARK_SHELL.equals(appResource) && !isSpecialCommand) {
       return buildPySparkShellCommand(env);
     } else if (SPARKR_SHELL.equals(appResource) && !isSpecialCommand) {
@@ -550,14 +560,6 @@ class SparkSubmitCommandBuilder extends 
AbstractCommandBuilder {
           checkArgument(value != null, "Missing argument to %s", CONF);
           String[] setConf = value.split("=", 2);
           checkArgument(setConf.length == 2, "Invalid argument to %s: %s", 
CONF, value);
-          // If both spark.remote and spark.mater are set, the error will be 
thrown later when
-          // the application is started.
-          if (setConf[0].equals("spark.remote")) {
-            isRemote = true;
-          } else if (setConf[0].equals(SparkLauncher.SPARK_API_MODE)) {
-            // Respects if the API mode is explicitly set.
-            isRemote = setConf[1].equalsIgnoreCase("connect");
-          }
           conf.put(setConf[0], setConf[1]);
         }
         case CLASS -> {
diff --git 
a/launcher/src/test/java/org/apache/spark/launcher/SparkSubmitCommandBuilderSuite.java
 
b/launcher/src/test/java/org/apache/spark/launcher/SparkSubmitCommandBuilderSuite.java
index 31318f1318ca..f0a295fb4228 100644
--- 
a/launcher/src/test/java/org/apache/spark/launcher/SparkSubmitCommandBuilderSuite.java
+++ 
b/launcher/src/test/java/org/apache/spark/launcher/SparkSubmitCommandBuilderSuite.java
@@ -18,12 +18,16 @@
 package org.apache.spark.launcher;
 
 import java.io.File;
+import java.io.FileReader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Properties;
 import java.util.regex.Pattern;
 
 import org.junit.jupiter.api.*;
@@ -33,29 +37,35 @@ import static org.junit.jupiter.api.Assertions.*;
 public class SparkSubmitCommandBuilderSuite extends BaseSuite {
 
   private static File dummyPropsFile;
+  private static File connectPropsFile;
   private static SparkSubmitOptionParser parser;
 
   @BeforeAll
   public static void setUp() throws Exception {
     dummyPropsFile = File.createTempFile("spark", "properties");
+    connectPropsFile = File.createTempFile("spark", "properties");
+    Files.writeString(connectPropsFile.toPath(), 
"spark.remote=sc://connect-server:15002");
     parser = new SparkSubmitOptionParser();
   }
 
   @AfterAll
   public static void cleanUp() throws Exception {
     dummyPropsFile.delete();
+    connectPropsFile.delete();
   }
 
   @Test
   public void testDriverCmdBuilder() throws Exception {
-    testCmdBuilder(true, true);
-    testCmdBuilder(true, false);
+    testCmdBuilder(true, null);
+    testCmdBuilder(true, dummyPropsFile);
+    testCmdBuilder(true, connectPropsFile);
   }
 
   @Test
   public void testClusterCmdBuilder() throws Exception {
-    testCmdBuilder(false, true);
-    testCmdBuilder(false, false);
+    testCmdBuilder(false, null);
+    testCmdBuilder(false, dummyPropsFile);
+    testCmdBuilder(false, connectPropsFile);
   }
 
   @Test
@@ -307,7 +317,7 @@ public class SparkSubmitCommandBuilderSuite extends 
BaseSuite {
     assertTrue(builder.isClientMode(userProps));
   }
 
-  private void testCmdBuilder(boolean isDriver, boolean 
useDefaultPropertyFile) throws Exception {
+  private void testCmdBuilder(boolean isDriver, File propertiesFile) throws 
Exception {
     final String DRIVER_DEFAULT_PARAM = "-Ddriver-default";
     final String DRIVER_EXTRA_PARAM = "-Ddriver-extra";
     String deployMode = isDriver ? "client" : "cluster";
@@ -325,16 +335,16 @@ public class SparkSubmitCommandBuilderSuite extends 
BaseSuite {
     launcher.appArgs.add("bar");
     launcher.conf.put("spark.foo", "foo");
     // either set the property through "--conf" or through default property 
file
-    if (!useDefaultPropertyFile) {
-      launcher.setPropertiesFile(dummyPropsFile.getAbsolutePath());
+    if (propertiesFile == null) {
+      launcher.childEnv.put("SPARK_CONF_DIR", 
System.getProperty("spark.test.home")
+          + "/launcher/src/test/resources");
+    } else {
+      launcher.setPropertiesFile(propertiesFile.getAbsolutePath());
       launcher.conf.put(SparkLauncher.DRIVER_MEMORY, "1g");
       launcher.conf.put(SparkLauncher.DRIVER_EXTRA_CLASSPATH, "/driver");
       launcher.conf.put(SparkLauncher.DRIVER_DEFAULT_JAVA_OPTIONS, 
DRIVER_DEFAULT_PARAM);
       launcher.conf.put(SparkLauncher.DRIVER_EXTRA_JAVA_OPTIONS, 
DRIVER_EXTRA_PARAM);
       launcher.conf.put(SparkLauncher.DRIVER_EXTRA_LIBRARY_PATH, "/native");
-    } else {
-      launcher.childEnv.put("SPARK_CONF_DIR", 
System.getProperty("spark.test.home")
-          + "/launcher/src/test/resources");
     }
 
     Map<String, String> env = new HashMap<>();
@@ -348,13 +358,7 @@ public class SparkSubmitCommandBuilderSuite extends 
BaseSuite {
         "Driver default options should be configured.");
       assertTrue(cmd.contains(DRIVER_EXTRA_PARAM), "Driver extra options 
should be configured.");
     } else {
-      boolean found = false;
-      for (String arg : cmd) {
-        if (arg.startsWith("-Xmx")) {
-          found = true;
-          break;
-        }
-      }
+      boolean found = cmd.stream().anyMatch(arg -> arg.startsWith("-Xmx"));
       assertFalse(found, "Memory arguments should not be set.");
       assertFalse(cmd.contains(DRIVER_DEFAULT_PARAM),
         "Driver default options should not be configured.");
@@ -379,8 +383,15 @@ public class SparkSubmitCommandBuilderSuite extends 
BaseSuite {
     }
 
     // Checks below are the same for both driver and non-driver mode.
-    if (!useDefaultPropertyFile) {
-      assertEquals(dummyPropsFile.getAbsolutePath(), findArgValue(cmd, 
parser.PROPERTIES_FILE));
+    if (propertiesFile != null) {
+      assertEquals(propertiesFile.getAbsolutePath(), findArgValue(cmd, 
parser.PROPERTIES_FILE));
+      try (FileReader reader = new FileReader(propertiesFile, 
StandardCharsets.UTF_8)) {
+        Properties props = new Properties();
+        props.load(reader);
+        if (props.containsKey("spark.remote")) {
+          assertTrue(launcher.isRemote);
+        }
+      }
     }
     assertEquals("yarn", findArgValue(cmd, parser.MASTER));
     assertEquals(deployMode, findArgValue(cmd, parser.DEPLOY_MODE));


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@spark.apache.org
For additional commands, e-mail: commits-h...@spark.apache.org

Reply via email to