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

eladkal pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new 88b83d1f0d4 Fix Spark submit standalone status polling to use correct 
REST endpoint (#71949)
88b83d1f0d4 is described below

commit 88b83d1f0d4134baf1133f967b83bdccdc685696
Author: hunnguye <[email protected]>
AuthorDate: Thu Oct 8 17:45:19 2026 +0200

    Fix Spark submit standalone status polling to use correct REST endpoint 
(#71949)
    
    * add rest_endpoint to connection dictionary
    
    * instead of calculating the rest endpoint, take it directly from the 
available attribute in the connection dictionary. Different than previously 
described, master URLs shouldn't be able to be a comma separated list of hosts.
    
    * add condition for master url ending with 7077 / the binary endpoint. In 
that case use the created rest_endpoint instead
    
    * formatting
    
    * refactor logic
    
    * refactor so spark rest endpoint for standalone server is constructed once
    
    * apply same logic as status request via rest endpoint to killing driver
    
    * formatting
    
    * remove condition on port 6066 to not break existing tests
    
    * added tests
    
    * formatting
    
    * fix rest_endpoint generation
    
    * make host name for rpc endpoint more verbose
    
    * formatting
    
    * reverting changes on get_job_status
    
    * more general approach to rest_endpoint usage and generation, independent 
from port
    
    * fix test coverage including rest_endpoint attribute. Add regression test 
for Spark standalone REST endpoint
    
    * formatting
    
    * only genereate rest_endpoint if there is axactly one master endpoint (non 
HA setup)
    
    * changed log level driver and job status command logging
    
    * add regression test for HA spark setup with multiple endpoints
    
    * Handle IPv6 in Spark REST endpoint construction and added regression tests
    
    * parameterize tests
    
    * Refactor Spark connection tests to reuse a default overridable connection 
dictionary as base
---
 .../providers/apache/spark/hooks/spark_submit.py   |  48 +-
 .../unit/apache/spark/hooks/test_spark_submit.py   | 491 ++++++++++++---------
 2 files changed, 328 insertions(+), 211 deletions(-)

diff --git 
a/providers/apache/spark/src/airflow/providers/apache/spark/hooks/spark_submit.py
 
b/providers/apache/spark/src/airflow/providers/apache/spark/hooks/spark_submit.py
index a0a65ccb11a..7905e9a5d68 100644
--- 
a/providers/apache/spark/src/airflow/providers/apache/spark/hooks/spark_submit.py
+++ 
b/providers/apache/spark/src/airflow/providers/apache/spark/hooks/spark_submit.py
@@ -480,6 +480,7 @@ class SparkSubmitHook(BaseHook, LoggingMixin):
             # fallback if connection lookup fails; overridden by 
rest-scheme/rest-port extras below
             "rest_scheme": "http",
             "rest_port": 6066,
+            "rest_endpoint": None,
         }
 
         try:
@@ -532,6 +533,17 @@ class SparkSubmitHook(BaseHook, LoggingMixin):
                     conn_data["keytab"] = 
self._create_keytab_path_from_base64_keytab(
                         base64_keytab, conn_data["principal"]
                     )
+            # Construct the Standalone Restendpoint
+            if (
+                conn.conn_type == "spark"
+                and conn_data["master"].startswith("spark://")
+                and conn_data["deploy_mode"] == "cluster"
+                and "," not in conn_data["master"]  # only consider single 
master, non-HA for now
+            ):
+                host = conn_data["master"].replace("spark://", "").strip()
+                conn_data["rest_endpoint"] = (
+                    f"{conn_data['rest_scheme']}://{host.rsplit(':', 
1)[0]}:{conn_data['rest_port']}"
+                )
         except AirflowException:
             self.log.info(
                 "Could not load connection string %s, defaulting to %s", 
self._conn_id, conn_data["master"]
@@ -733,16 +745,15 @@ class SparkSubmitHook(BaseHook, LoggingMixin):
         :return: full command to be executed
         """
         curl_max_wait_time = 30
-        spark_host = self._connection["master"]
-        if spark_host.endswith(":6066"):
-            spark_host = spark_host.replace("spark://", "http://";)
+        if self._connection["rest_endpoint"]:
+            spark_host = self._connection["rest_endpoint"]
             connection_cmd = [
                 "/usr/bin/curl",
                 "--max-time",
                 str(curl_max_wait_time),
                 f"{spark_host}/v1/submissions/status/{self._driver_id}",
             ]
-            self.log.info(connection_cmd)
+            self.log.debug(connection_cmd)
 
             # The driver id so we can poll for its status
             if not self._driver_id:
@@ -1354,17 +1365,30 @@ class SparkSubmitHook(BaseHook, LoggingMixin):
 
         :return: full command to kill a driver
         """
-        # Assume that spark-submit is present in the path to the executing user
-        connection_cmd = [self._connection["spark_binary"]]
+        curl_max_wait_time = 30
+        if self._connection["rest_endpoint"]:
+            spark_host = self._connection["rest_endpoint"]
+            connection_cmd = [
+                "/usr/bin/curl",
+                "--max-time",
+                str(curl_max_wait_time),
+                "-X",
+                "POST",
+                f"{spark_host}/v1/submissions/kill/{self._driver_id}",
+            ]
+            self.log.debug(connection_cmd)
 
-        # The url to the spark master
-        connection_cmd += ["--master", self._connection["master"]]
+        else:
+            connection_cmd = self._get_spark_binary_path()
 
-        # The actual kill command
-        if self._driver_id:
-            connection_cmd += ["--kill", self._driver_id]
+            # The url to the spark master
+            connection_cmd += ["--master", self._connection["master"]]
+
+            # The actual kill command
+            if self._driver_id:
+                connection_cmd += ["--kill", self._driver_id]
 
-        self.log.debug("Spark-Kill cmd: %s", connection_cmd)
+            self.log.debug("Spark-Kill cmd: %s", connection_cmd)
 
         return connection_cmd
 
diff --git 
a/providers/apache/spark/tests/unit/apache/spark/hooks/test_spark_submit.py 
b/providers/apache/spark/tests/unit/apache/spark/hooks/test_spark_submit.py
index 52060da0b35..18c2a7adee9 100644
--- a/providers/apache/spark/tests/unit/apache/spark/hooks/test_spark_submit.py
+++ b/providers/apache/spark/tests/unit/apache/spark/hooks/test_spark_submit.py
@@ -157,6 +157,38 @@ class TestSparkSubmitHook:
                 extra='{"deploy-mode": "cluster"}',
             )
         )
+        create_connection_without_db(
+            Connection(
+                conn_id="spark_standalone_cluster_rpc_endpoint",
+                conn_type="spark",
+                host="spark://spark-standalone-master-rpc-endpoint:7077",
+                extra='{"deploy-mode": "cluster"}',
+            )
+        )
+        create_connection_without_db(
+            Connection(
+                conn_id="spark_standalone_cluster_ha",
+                conn_type="spark",
+                host="spark://m1:6066,m2:6066",
+                extra={"deploy-mode": "cluster"},
+            )
+        )
+        create_connection_without_db(
+            Connection(
+                conn_id="spark_standalone_cluster_ipv6",
+                conn_type="spark",
+                host="spark://[2001:db8::1]:6066",
+                extra={"deploy-mode": "cluster"},
+            )
+        )
+        create_connection_without_db(
+            Connection(
+                conn_id="spark_standalone_cluster_ipv6_ha",
+                conn_type="spark",
+                host="spark://[2001:db8::1]:6066,[1993:db8::1]:6066",
+                extra={"deploy-mode": "cluster"},
+            )
+        )
         create_connection_without_db(
             Connection(
                 conn_id="spark_standalone_cluster_client_mode",
@@ -321,41 +353,81 @@ class TestSparkSubmitHook:
         ):
             hook._build_spark_submit_command(self._spark_job_file)
 
-    def test_build_track_driver_status_command(self):
+    @pytest.mark.parametrize(
+        ("conn_id", "expected_command"),
+        [
+            (
+                "spark_standalone_cluster",
+                [
+                    "/usr/bin/curl",
+                    "--max-time",
+                    "30",
+                    
"http://spark-standalone-master:6066/v1/submissions/status/driver-1";,
+                ],
+            ),
+            (
+                "spark_yarn_cluster",
+                [
+                    "spark-submit",
+                    "--master",
+                    "yarn://yarn-master",
+                    "--status",
+                    "driver-1",
+                ],
+            ),
+            (
+                "spark_standalone_cluster_rpc_endpoint",
+                [
+                    "/usr/bin/curl",
+                    "--max-time",
+                    "30",
+                    
"http://spark-standalone-master-rpc-endpoint:6066/v1/submissions/status/driver-1";,
+                ],
+            ),
+            (
+                "spark_standalone_cluster_ha",
+                [
+                    "spark-submit",
+                    "--master",
+                    "spark://m1:6066,m2:6066",
+                    "--status",
+                    "driver-1",
+                ],
+            ),
+            (
+                "spark_standalone_cluster_ipv6",
+                [
+                    "/usr/bin/curl",
+                    "--max-time",
+                    "30",
+                    "http://[2001:db8::1]:6066/v1/submissions/status/driver-1";,
+                ],
+            ),
+            (
+                "spark_standalone_cluster_ipv6_ha",
+                [
+                    "spark-submit",
+                    "--master",
+                    "spark://[2001:db8::1]:6066,[1993:db8::1]:6066",
+                    "--status",
+                    "driver-1",
+                ],
+            ),
+        ],
+    )
+    def test_build_track_driver_status_command(self, conn_id, 
expected_command):
         # note this function is only relevant for spark setup matching below 
condition
         # 'spark://' in self._connection['master'] and 
self._connection['deploy_mode'] == 'cluster'
 
         # Given
-        hook_spark_standalone_cluster = 
SparkSubmitHook(conn_id="spark_standalone_cluster")
-        hook_spark_standalone_cluster._driver_id = "driver-20171128111416-0001"
-        hook_spark_yarn_cluster = SparkSubmitHook(conn_id="spark_yarn_cluster")
-        hook_spark_yarn_cluster._driver_id = "driver-20171128111417-0001"
+        hook = SparkSubmitHook(conn_id=conn_id)
+        hook._driver_id = "driver-1"
 
         # When
-        build_track_driver_status_spark_standalone_cluster = (
-            hook_spark_standalone_cluster._build_track_driver_status_command()
-        )
-        build_track_driver_status_spark_yarn_cluster = (
-            hook_spark_yarn_cluster._build_track_driver_status_command()
-        )
+        build_track_driver_status = hook._build_track_driver_status_command()
 
         # Then
-        expected_spark_standalone_cluster = [
-            "/usr/bin/curl",
-            "--max-time",
-            "30",
-            
"http://spark-standalone-master:6066/v1/submissions/status/driver-20171128111416-0001";,
-        ]
-        expected_spark_yarn_cluster = [
-            "spark-submit",
-            "--master",
-            "yarn://yarn-master",
-            "--status",
-            "driver-20171128111417-0001",
-        ]
-
-        assert expected_spark_standalone_cluster == 
build_track_driver_status_spark_standalone_cluster
-        assert expected_spark_yarn_cluster == 
build_track_driver_status_spark_yarn_cluster
+        assert build_track_driver_status == expected_command
 
     @pytest.mark.db_test
     
@patch("airflow.providers.apache.spark.hooks.spark_submit.subprocess.Popen")
@@ -442,6 +514,24 @@ class TestSparkSubmitHook:
         assert should_track_driver_status_spark_binary_set is False
         assert should_track_driver_status_spark_standalone_cluster is True
 
+    @staticmethod
+    def expected_resolved_connection(**overrides):
+        # default connection attributes; override values as needed per test.
+        connection = {
+            "master": "yarn",
+            "spark_binary": None,
+            "deploy_mode": None,
+            "queue": None,
+            "namespace": None,
+            "principal": None,
+            "keytab": None,
+            "rest_scheme": "http",
+            "rest_port": 6066,
+            "rest_endpoint": None,
+        }
+        connection.update(overrides)
+        return connection
+
     @pytest.mark.db_test
     def test_resolve_connection_yarn_default(self, sdk_connection_not_found):
         # Given
@@ -453,17 +543,7 @@ class TestSparkSubmitHook:
 
         # Then
         dict_cmd = self.cmd_args_to_dict(cmd)
-        expected_spark_connection = {
-            "master": "yarn",
-            "spark_binary": "spark-submit",
-            "deploy_mode": None,
-            "queue": None,
-            "namespace": None,
-            "principal": None,
-            "keytab": None,
-            "rest_scheme": "http",
-            "rest_port": 6066,
-        }
+        expected_spark_connection = 
self.expected_resolved_connection(spark_binary="spark-submit")
         assert connection == expected_spark_connection
         assert dict_cmd["--master"] == "yarn"
 
@@ -478,17 +558,10 @@ class TestSparkSubmitHook:
 
         # Then
         dict_cmd = self.cmd_args_to_dict(cmd)
-        expected_spark_connection = {
-            "master": "yarn",
-            "spark_binary": "spark-submit",
-            "deploy_mode": None,
-            "queue": "root.default",
-            "namespace": None,
-            "principal": None,
-            "keytab": None,
-            "rest_scheme": "http",
-            "rest_port": 6066,
-        }
+        expected_spark_connection = self.expected_resolved_connection(
+            spark_binary="spark-submit",
+            queue="root.default",
+        )
         assert connection == expected_spark_connection
         assert dict_cmd["--master"] == "yarn"
         assert dict_cmd["--queue"] == "root.default"
@@ -503,17 +576,10 @@ class TestSparkSubmitHook:
 
         # Then
         dict_cmd = self.cmd_args_to_dict(cmd)
-        expected_spark_connection = {
-            "master": "mesos://host:5050",
-            "spark_binary": "spark-submit",
-            "deploy_mode": None,
-            "queue": None,
-            "namespace": None,
-            "principal": None,
-            "keytab": None,
-            "rest_scheme": "http",
-            "rest_port": 6066,
-        }
+        expected_spark_connection = self.expected_resolved_connection(
+            master="mesos://host:5050",
+            spark_binary="spark-submit",
+        )
         assert connection == expected_spark_connection
         assert dict_cmd["--master"] == "mesos://host:5050"
 
@@ -527,17 +593,12 @@ class TestSparkSubmitHook:
 
         # Then
         dict_cmd = self.cmd_args_to_dict(cmd)
-        expected_spark_connection = {
-            "master": "yarn://yarn-master",
-            "spark_binary": "spark-submit",
-            "deploy_mode": "cluster",
-            "queue": "root.etl",
-            "namespace": None,
-            "principal": None,
-            "keytab": None,
-            "rest_scheme": "http",
-            "rest_port": 6066,
-        }
+        expected_spark_connection = self.expected_resolved_connection(
+            master="yarn://yarn-master",
+            spark_binary="spark-submit",
+            deploy_mode="cluster",
+            queue="root.etl",
+        )
         assert connection == expected_spark_connection
         assert dict_cmd["--master"] == "yarn://yarn-master"
         assert dict_cmd["--queue"] == "root.etl"
@@ -553,17 +614,12 @@ class TestSparkSubmitHook:
 
         # Then
         dict_cmd = self.cmd_args_to_dict(cmd)
-        expected_spark_connection = {
-            "queue": None,
-            "spark_binary": "spark-submit",
-            "master": "k8s://https://k8s-master";,
-            "deploy_mode": "cluster",
-            "namespace": "mynamespace",
-            "principal": None,
-            "keytab": None,
-            "rest_scheme": "http",
-            "rest_port": 6066,
-        }
+        expected_spark_connection = self.expected_resolved_connection(
+            master="k8s://https://k8s-master";,
+            spark_binary="spark-submit",
+            deploy_mode="cluster",
+            namespace="mynamespace",
+        )
         assert connection == expected_spark_connection
         assert dict_cmd["--master"] == "k8s://https://k8s-master";
         assert dict_cmd["--deploy-mode"] == "cluster"
@@ -581,17 +637,12 @@ class TestSparkSubmitHook:
 
         # Then
         dict_cmd = self.cmd_args_to_dict(cmd)
-        expected_spark_connection = {
-            "queue": None,
-            "spark_binary": "spark-submit",
-            "master": "k8s://https://k8s-master";,
-            "deploy_mode": "cluster",
-            "namespace": "airflow",
-            "principal": None,
-            "keytab": None,
-            "rest_scheme": "http",
-            "rest_port": 6066,
-        }
+        expected_spark_connection = self.expected_resolved_connection(
+            master="k8s://https://k8s-master";,
+            spark_binary="spark-submit",
+            deploy_mode="cluster",
+            namespace="airflow",
+        )
         assert connection == expected_spark_connection
         assert dict_cmd["--master"] == "k8s://https://k8s-master";
         assert dict_cmd["--deploy-mode"] == "cluster"
@@ -606,17 +657,9 @@ class TestSparkSubmitHook:
         cmd = hook._build_spark_submit_command(self._spark_job_file)
 
         # Then
-        expected_spark_connection = {
-            "master": "yarn",
-            "spark_binary": "spark2-submit",
-            "deploy_mode": None,
-            "queue": None,
-            "namespace": None,
-            "principal": None,
-            "keytab": None,
-            "rest_scheme": "http",
-            "rest_port": 6066,
-        }
+        expected_spark_connection = self.expected_resolved_connection(
+            spark_binary="spark2-submit",
+        )
         assert connection == expected_spark_connection
         assert cmd[0] == "spark2-submit"
 
@@ -629,17 +672,9 @@ class TestSparkSubmitHook:
         cmd = hook._build_spark_submit_command(self._spark_job_file)
 
         # Then
-        expected_spark_connection = {
-            "master": "yarn",
-            "spark_binary": "spark3-submit",
-            "deploy_mode": None,
-            "queue": None,
-            "namespace": None,
-            "principal": None,
-            "keytab": None,
-            "rest_scheme": "http",
-            "rest_port": 6066,
-        }
+        expected_spark_connection = self.expected_resolved_connection(
+            spark_binary="spark3-submit",
+        )
         assert connection == expected_spark_connection
         assert cmd[0] == "spark3-submit"
 
@@ -691,17 +726,9 @@ class TestSparkSubmitHook:
         cmd = hook._build_spark_submit_command(self._spark_job_file)
 
         # Then
-        expected_spark_connection = {
-            "master": "yarn",
-            "spark_binary": "spark3-submit",
-            "deploy_mode": None,
-            "queue": None,
-            "namespace": None,
-            "principal": None,
-            "keytab": None,
-            "rest_scheme": "http",
-            "rest_port": 6066,
-        }
+        expected_spark_connection = self.expected_resolved_connection(
+            spark_binary="spark3-submit",
+        )
         assert connection == expected_spark_connection
         assert cmd[0] == "spark3-submit"
 
@@ -715,17 +742,10 @@ class TestSparkSubmitHook:
         cmd = hook._build_spark_submit_command(self._spark_job_file)
 
         # Then
-        expected_spark_connection = {
-            "master": "yarn",
-            "spark_binary": "spark-submit",
-            "deploy_mode": None,
-            "queue": "root.default",
-            "namespace": None,
-            "principal": None,
-            "keytab": None,
-            "rest_scheme": "http",
-            "rest_port": 6066,
-        }
+        expected_spark_connection = self.expected_resolved_connection(
+            spark_binary="spark-submit",
+            queue="root.default",
+        )
         assert connection == expected_spark_connection
         assert cmd[0] == "spark-submit"
 
@@ -738,20 +758,67 @@ class TestSparkSubmitHook:
         cmd = hook._build_spark_submit_command(self._spark_job_file)
 
         # Then
-        expected_spark_connection = {
-            "master": "spark://spark-standalone-master:6066",
-            "spark_binary": "spark-submit",
-            "deploy_mode": "cluster",
-            "queue": None,
-            "namespace": None,
-            "principal": None,
-            "keytab": None,
-            "rest_scheme": "http",
-            "rest_port": 6066,
-        }
+        expected_spark_connection = self.expected_resolved_connection(
+            master="spark://spark-standalone-master:6066",
+            spark_binary="spark-submit",
+            deploy_mode="cluster",
+            rest_endpoint="http://spark-standalone-master:6066";,
+        )
         assert connection == expected_spark_connection
         assert cmd[0] == "spark-submit"
 
+    @pytest.mark.parametrize(
+        ("master", "rest_scheme", "rest_port", "expected"),
+        [
+            (
+                "spark://spark-standalone-master-rpc-endpoint:7078",
+                "http",
+                6067,
+                "http://spark-standalone-master-rpc-endpoint:6067";,
+            ),
+            (
+                "spark://spark-standalone-master-rpc-endpoint:7077",
+                "https",
+                7443,
+                "https://spark-standalone-master-rpc-endpoint:7443";,
+            ),
+        ],
+    )
+    def 
test_resolve_connection_spark_standalone_cluster_connection_rpc_endpoint(
+        self,
+        create_connection_without_db,
+        master,
+        rest_scheme,
+        rest_port,
+        expected,
+    ):
+        create_connection_without_db(
+            Connection(
+                conn_id="spark_standalone_cluster_rpc_endpoint_parametrized",
+                conn_type="spark",
+                host=master,
+                extra={
+                    "deploy-mode": "cluster",
+                    "rest-scheme": rest_scheme,
+                    "rest-port": rest_port,
+                },
+            )
+        )
+        # Given
+        hook = 
SparkSubmitHook(conn_id="spark_standalone_cluster_rpc_endpoint_parametrized")
+
+        # When
+        connection = hook._resolve_connection()
+        cmd = hook._build_spark_submit_command(self._spark_job_file)
+
+        # Then
+
+        assert connection["rest_endpoint"] == expected
+        assert connection["master"] == master
+        assert connection["rest_scheme"] == rest_scheme
+        assert connection["rest_port"] == rest_port
+        assert cmd[0] == "spark-submit"
+
     def test_resolve_connection_principal_set_connection(self):
         # Given
         hook = SparkSubmitHook(conn_id="spark_principal_set")
@@ -762,17 +829,10 @@ class TestSparkSubmitHook:
 
         # Then
         dict_cmd = self.cmd_args_to_dict(cmd)
-        expected_spark_connection = {
-            "master": "yarn",
-            "spark_binary": "spark-submit",
-            "deploy_mode": None,
-            "queue": None,
-            "namespace": None,
-            "principal": "user/[email protected]",
-            "keytab": None,
-            "rest_scheme": "http",
-            "rest_port": 6066,
-        }
+        expected_spark_connection = self.expected_resolved_connection(
+            spark_binary="spark-submit",
+            principal="user/[email protected]",
+        )
         assert connection == expected_spark_connection
         assert dict_cmd["--principal"] == "user/[email protected]"
 
@@ -786,17 +846,10 @@ class TestSparkSubmitHook:
 
         # Then
         dict_cmd = self.cmd_args_to_dict(cmd)
-        expected_spark_connection = {
-            "master": "yarn",
-            "spark_binary": "spark-submit",
-            "deploy_mode": None,
-            "queue": None,
-            "namespace": None,
-            "principal": "will-override",
-            "keytab": None,
-            "rest_scheme": "http",
-            "rest_port": 6066,
-        }
+        expected_spark_connection = self.expected_resolved_connection(
+            spark_binary="spark-submit",
+            principal="will-override",
+        )
         assert connection == expected_spark_connection
         assert dict_cmd["--principal"] == "will-override"
 
@@ -814,17 +867,10 @@ class TestSparkSubmitHook:
 
         # Then
         dict_cmd = self.cmd_args_to_dict(cmd)
-        expected_spark_connection = {
-            "master": "yarn",
-            "spark_binary": "spark-submit",
-            "deploy_mode": None,
-            "queue": None,
-            "namespace": None,
-            "principal": None,
-            "keytab": "privileged_user.keytab",
-            "rest_scheme": "http",
-            "rest_port": 6066,
-        }
+        expected_spark_connection = self.expected_resolved_connection(
+            spark_binary="spark-submit",
+            keytab="privileged_user.keytab",
+        )
         assert connection == expected_spark_connection
         assert dict_cmd["--keytab"] == "privileged_user.keytab"
 
@@ -841,17 +887,10 @@ class TestSparkSubmitHook:
 
         # Then
         dict_cmd = self.cmd_args_to_dict(cmd)
-        expected_spark_connection = {
-            "master": "yarn",
-            "spark_binary": "spark-submit",
-            "deploy_mode": None,
-            "queue": None,
-            "namespace": None,
-            "principal": None,
-            "keytab": "will-override",
-            "rest_scheme": "http",
-            "rest_port": 6066,
-        }
+        expected_spark_connection = self.expected_resolved_connection(
+            spark_binary="spark-submit",
+            keytab="will-override",
+        )
         assert connection == expected_spark_connection
         assert dict_cmd["--keytab"] == "will-override"
         assert not mock_create_keytab_path_from_base64_keytab.called, (
@@ -1180,7 +1219,65 @@ class TestSparkSubmitHook:
         submit_process.kill.assert_not_called()
         mock_popen.assert_not_called()
 
-    def test_standalone_cluster_process_on_kill(self):
+    @pytest.mark.parametrize(
+        ("conn_id", "expected_command"),
+        [
+            (
+                "spark_standalone_cluster",
+                [
+                    "/usr/bin/curl",
+                    "--max-time",
+                    "30",
+                    "-X",
+                    "POST",
+                    
"http://spark-standalone-master:6066/v1/submissions/kill/driver-20171128111415-0001";,
+                ],
+            ),
+            (
+                "spark_standalone_cluster_rpc_endpoint",
+                [
+                    "/usr/bin/curl",
+                    "--max-time",
+                    "30",
+                    "-X",
+                    "POST",
+                    
"http://spark-standalone-master-rpc-endpoint:6066/v1/submissions/kill/driver-20171128111415-0001";,
+                ],
+            ),
+            (
+                "spark_standalone_cluster_ha",
+                [
+                    "spark-submit",
+                    "--master",
+                    "spark://m1:6066,m2:6066",
+                    "--kill",
+                    "driver-20171128111415-0001",
+                ],
+            ),
+            (
+                "spark_standalone_cluster_ipv6",
+                [
+                    "/usr/bin/curl",
+                    "--max-time",
+                    "30",
+                    "-X",
+                    "POST",
+                    
"http://[2001:db8::1]:6066/v1/submissions/kill/driver-20171128111415-0001";,
+                ],
+            ),
+            (
+                "spark_standalone_cluster_ipv6_ha",
+                [
+                    "spark-submit",
+                    "--master",
+                    "spark://[2001:db8::1]:6066,[1993:db8::1]:6066",
+                    "--kill",
+                    "driver-20171128111415-0001",
+                ],
+            ),
+        ],
+    )
+    def test_standalone_cluster_process_on_kill(self, conn_id, 
expected_command):
         # Given
         log_lines = [
             "Running Spark using the REST application submission protocol.",
@@ -1189,18 +1286,14 @@ class TestSparkSubmitHook:
             "17/11/28 11:14:15 INFO RestSubmissionClient: Submission 
successfully "
             "created as driver-20171128111415-0001. Polling submission 
state...",
         ]
-        hook = SparkSubmitHook(conn_id="spark_standalone_cluster")
+        hook = SparkSubmitHook(conn_id=conn_id)
         hook._process_spark_submit_log(log_lines)
 
         # When
         kill_cmd = hook._build_spark_driver_kill_command()
 
         # Then
-        assert kill_cmd[0] == "spark-submit"
-        assert kill_cmd[1] == "--master"
-        assert kill_cmd[2] == "spark://spark-standalone-master:6066"
-        assert kill_cmd[3] == "--kill"
-        assert kill_cmd[4] == "driver-20171128111415-0001"
+        assert kill_cmd == expected_command
 
     @patch("airflow.providers.cncf.kubernetes.kube_client.get_kube_client")
     
@patch("airflow.providers.apache.spark.hooks.spark_submit.subprocess.Popen")

Reply via email to