wangyum commented on code in PR #57525:
URL: https://github.com/apache/spark/pull/57525#discussion_r3675994109


##########
core/src/test/scala/org/apache/spark/scheduler/CoarseGrainedSchedulerBackendSuite.scala:
##########
@@ -606,6 +606,143 @@ class CoarseGrainedSchedulerBackendSuite extends 
SparkFunSuite with LocalSparkCo
     assert(mockEndpointRef.decommissionReceived)
   }
 
+  test("SPARK-58322: reject RegisterExecutor with mismatched app ID") {
+    val conf = new SparkConf()
+      .setMaster("local-cluster[0, 3, 1024]")
+      .setAppName("test")
+    sc = new SparkContext(conf)
+    val backend = 
sc.schedulerBackend.asInstanceOf[CoarseGrainedSchedulerBackend]
+    val mockEndpointRef = mock[RpcEndpointRef]
+    val mockAddress = mock[RpcAddress]
+
+    val ex = intercept[SparkException] {
+      backend.driverEndpoint.askSync[Boolean](
+        RegisterExecutor("1", mockEndpointRef, mockAddress.host, 1, Map.empty, 
Map.empty,
+          Map.empty, ResourceProfile.DEFAULT_RESOURCE_PROFILE_ID, 
"wrong-app-id"))
+    }
+    assert(ex.getCause.getMessage.contains("Executor app ID wrong-app-id does 
not match"))
+  }
+
+  test("SPARK-58322: accept RegisterExecutor with matching app ID") {
+    val conf = new SparkConf()
+      .setMaster("local-cluster[0, 3, 1024]")
+      .setAppName("test")
+    sc = new SparkContext(conf)
+    val backend = 
sc.schedulerBackend.asInstanceOf[CoarseGrainedSchedulerBackend]
+    val mockEndpointRef = mock[RpcEndpointRef]
+    val mockAddress = mock[RpcAddress]
+
+    val result = backend.driverEndpoint.askSync[Boolean](
+      RegisterExecutor("1", mockEndpointRef, mockAddress.host, 1, Map.empty, 
Map.empty,
+        Map.empty, ResourceProfile.DEFAULT_RESOURCE_PROFILE_ID, 
sc.applicationId))
+    assert(result)
+  }
+
+  test("SPARK-58322: accept RegisterExecutor with null app ID for backward 
compatibility") {
+    val conf = new SparkConf()
+      .setMaster("local-cluster[0, 3, 1024]")
+      .setAppName("test")
+    sc = new SparkContext(conf)
+    val backend = 
sc.schedulerBackend.asInstanceOf[CoarseGrainedSchedulerBackend]
+    val mockEndpointRef = mock[RpcEndpointRef]
+    val mockAddress = mock[RpcAddress]
+
+    val result = backend.driverEndpoint.askSync[Boolean](
+      RegisterExecutor("1", mockEndpointRef, mockAddress.host, 1, Map.empty, 
Map.empty,
+        Map.empty, ResourceProfile.DEFAULT_RESOURCE_PROFILE_ID, null))
+    assert(result)
+  }
+
+  test("SPARK-58322: reject RegisterExecutor after driver swap between config 
fetch and " +
+    "registration") {
+    // Simulate the port-reuse scenario: executor fetches config from driver 
A, then driver A
+    // dies and releases its RPC port, driver B binds the same address, and 
the executor sends
+    // RegisterExecutor to driver B. The app ID carried by the executor (from 
driver A) will not
+    // match driver B's applicationId, so registration must be rejected.
+    val driverAAppId = "app-driver-A"
+
+    // Set up a fake "driver A" endpoint that responds to 
RetrieveSparkAppConfig.
+    val driverRpcEnv = RpcEnv.create("test-driverA", "localhost", 0, new 
SparkConf(),
+      new SecurityManager(new SparkConf()), clientMode = false)
+    try {
+      driverRpcEnv.setupEndpoint("fake-driverA", new RpcEndpoint {
+        override val rpcEnv: RpcEnv = driverRpcEnv
+        override def receiveAndReply(context: RpcCallContext): 
PartialFunction[Any, Unit] = {
+          case RetrieveSparkAppConfig(_) =>
+            context.reply(SparkAppConfig(
+              Seq("spark.app.id" -> driverAAppId),
+              None, None, ResourceProfile.getOrCreateDefaultProfile(new 
SparkConf()), None))
+        }
+      })
+
+      // Executor fetches config from driver A.
+      val driverARef = 
driverRpcEnv.setupEndpointRefByURI("spark://fake-driverA@localhost:" +
+        driverRpcEnv.address.port)
+      val cfg = driverARef.askSync[SparkAppConfig](
+        RetrieveSparkAppConfig(ResourceProfile.DEFAULT_RESOURCE_PROFILE_ID))
+      val fetchedAppId = cfg.sparkProperties.find(_._1 == 
"spark.app.id").map(_._2).orNull
+      assert(fetchedAppId == driverAAppId)
+
+      // Now create driver B (a real SparkContext with a different 
applicationId).
+      val conf = new SparkConf()
+        .setMaster("local-cluster[0, 3, 1024]")
+        .setAppName("test")
+      sc = new SparkContext(conf)
+      val backend = 
sc.schedulerBackend.asInstanceOf[CoarseGrainedSchedulerBackend]
+      assert(sc.applicationId != driverAAppId)
+
+      // Executor sends RegisterExecutor to driver B with the app ID from 
driver A.
+      val mockEndpointRef = mock[RpcEndpointRef]
+      val mockAddress = mock[RpcAddress]
+      val ex = intercept[SparkException] {
+        backend.driverEndpoint.askSync[Boolean](
+          RegisterExecutor("1", mockEndpointRef, mockAddress.host, 1, 
Map.empty, Map.empty,
+            Map.empty, ResourceProfile.DEFAULT_RESOURCE_PROFILE_ID, 
fetchedAppId))

Review Comment:
   👍



-- 
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