dongjoon-hyun commented on code in PR #58696:
URL: https://github.com/apache/spark/pull/58696#discussion_r4057909934


##########
core/src/main/scala/org/apache/spark/deploy/Client.scala:
##########
@@ -275,6 +277,24 @@ object Client {
     // scalastyle:on println
     new ClientApp().start(args, new SparkConf())
   }
+
+  /**
+   * Environment variables to forward to the driver. Only variables whose name 
starts with
+   * `SPARK_` are forwarded, excluding `SPARK_ENV_LOADED`, `SPARK_HOME`, 
`SPARK_CONF_DIR`,
+   * `SPARK_LOCAL_IP` and `SPARK_LOCAL_HOSTNAME`. The rule is defined by
+   * `RestSubmissionClient.filterSystemEnvironment`, so this matches the REST 
submission gateway.
+   * If `spark.standalone.submit.filterEnvironment` is disabled, the full 
environment of the
+   * submitting process is forwarded instead.
+   */
+  private[deploy] def driverEnvironment(

Review Comment:
   One flag is covering two unrelated concerns here: dropping non-Spark 
variables (the point of this PR) and dropping the host-specific 
`SPARK_LOCAL_IP` / `SPARK_LOCAL_HOSTNAME`.
   
   With `spark.standalone.submit.filterEnvironment=false` the full `sys.env` is 
forwarded again, which brings back exactly what SPARK-20025 fixed for the REST 
path. Concretely: an operator on a bare-metal cluster without DNS sets 
`SPARK_LOCAL_IP` on the submitting host, then flips this to `false` to get 
`JAVA_HOME` / `HADOOP_CONF_DIR` back. The driver is then launched on a worker 
with `SPARK_LOCAL_IP` pointing at the submitter's address and fails with 
`BindException: Cannot assign requested address`; with `--supervise`, every 
failover onto another node fails the same way.
   
   Could we always drop `SPARK_LOCAL_(IP|HOSTNAME)` and let the flag control 
only the `SPARK_` prefix filtering? That keeps the escape hatch for the new 
behavior without reopening a known bug.



##########
core/src/main/scala/org/apache/spark/deploy/rest/StandaloneRestServer.scala:
##########
@@ -231,6 +231,10 @@ private[rest] class StandaloneSubmitRequestServlet(
       _.replace(s":$masterRestPort", s":$masterPort")).getOrElse(masterUrl)
     val appArgs = Option(request.appArgs).getOrElse(Array[String]())
     // Filter SPARK_LOCAL_(IP|HOSTNAME) environment variables from being set 
on the remote system.
+    // Since SPARK-59404, RestSubmissionClient.filterSystemEnvironment already 
drops these on the
+    // client side, so this filter is duplicated. It is kept for backward 
compatibility with
+    // older clients and with requests that do not come from 
RestSubmissionClient.
+    // TODO(SPARK-59669): Remove this duplicated filter in Spark 5.x.

Review Comment:
   I don't think this filter is duplicated, and I'd rather not schedule its 
removal.
   
   `RestSubmissionClient.filterSystemEnvironment` runs in the submitting 
client's JVM. On the cluster side, neither `Master` nor `Worker` filters 
`DriverDescription.command.environment` -- grepping for `SPARK_LOCAL` under 
`core/src/main/scala/org/apache/spark/deploy/` returns only this one site. So 
this is the only place the cluster sanitizes a client-supplied environment, not 
a redundant copy of a client-side check.
   
   If the TODO is acted on, any HTTP client posting to the Master REST port can 
put `SPARK_LOCAL_IP` into `environmentVariables`, and the Worker passes it 
straight into the driver's `ProcessBuilder`. An unmodified older `spark-submit` 
reproduces it without any malicious intent. The second half of the comment 
already says as much ("kept for backward compatibility with older clients"), 
which reads as contradicting the TODO.
   
   Suggest dropping the TODO line and describing this as the server-side check 
instead.



##########
core/src/test/scala/org/apache/spark/deploy/ClientSuite.scala:
##########
@@ -48,4 +56,73 @@ class ClientSuite extends SparkFunSuite with Matchers {
     // Invalid syntax.
     ClientArguments.isValidJarUrl("hdfs:") should be (false)
   }
+
+  /**
+   * Launches a driver through a [[ClientEndpoint]] wired to a fake master and 
returns the
+   * [[Command]] carried by the [[RequestSubmitDriver]] message the client 
sends.
+   */
+  private def submittedCommand(conf: SparkConf): Command = {
+    val env = RpcEnv.create("ClientSuite", "localhost", 0, conf, new 
SecurityManager(conf))
+    try {
+      val submitted = Promise[RequestSubmitDriver]()
+      val master = env.setupEndpoint(Master.ENDPOINT_NAME, new RpcEndpoint {
+        override val rpcEnv: RpcEnv = env
+        // Record the submission without replying, so the client neither polls 
the driver
+        // status nor exits the JVM.
+        override def receiveAndReply(context: RpcCallContext): 
PartialFunction[Any, Unit] = {
+          case request: RequestSubmitDriver => submitted.success(request)
+        }
+      })
+      val args = new ClientArguments(
+        Array("launch", "spark://localhost:7077", "file:///path/to/app.jar", 
"MainClass"))
+      env.setupEndpoint("client", new ClientEndpoint(env, args, Seq(master), 
conf))

Review Comment:
   This instantiates a real `ClientEndpoint`, whose `onError` calls 
`System.exit(-1)`. `Inbox`'s `safelyCall` routes anything thrown from `onStart` 
into `onError`, so a failure inside `onStart` -- say 
`ResourceUtils.parseResourceRequirements` throwing because a 
`spark.driver.resource.*` system property leaked in from an earlier suite in 
the same forked JVM -- terminates the whole `core` test JVM instead of failing 
this test, and surfaces as an opaque JVM exit with no pointer back here.
   
   Using `new SparkConf(false)` removes the system-property path and makes 
these two tests deterministic.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to