You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@spark.apache.org by do...@apache.org on 2026/10/19 21:53:19 UTC

(spark) branch branch-4.x updated: [SPARK-60342][CORE][UI] Respect UI root in Master UI links

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

dongjoon-hyun 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 22ed2349c008 [SPARK-60342][CORE][UI] Respect UI root in Master UI links
22ed2349c008 is described below

commit 22ed2349c008a8f1ae41ec42d3add816a29d0a1a
Author: Dongjoon Hyun <do...@apache.org>
AuthorDate: Mon Oct 19 14:52:56 2026 -0700

    [SPARK-60342][CORE][UI] Respect UI root in Master UI links
    
    ### What changes were proposed in this pull request?
    
    This PR aims to respect the UI root (`spark.ui.proxyBase`, `APPLICATION_WEB_PROXY_BASE`, or the Knox `X-Forwarded-Context` header) in the Master UI links, like SPARK-60311 did in the History Server log page.
    
    - `MasterPage`: The `Environment` and `Log` links use `UIUtils.prependBaseUri`.
    - `EnvironmentPage` and `LogPage`: The `Back to Master` links use `UIUtils.prependBaseUri`.
    
    ### Why are the changes needed?
    
    Under a UI root, e.g., behind a front-end reverse proxy with a path (`spark.ui.reverseProxyUrl`) or Knox, these links go outside of the UI root and don't work. This bug exists since Apache Spark [v4.0.0](https://github.com/apache/spark/releases/tag/v4.0.0) (2025-05-19), which added the Master log page (SPARK-46870) and environment page (SPARK-47894).
    
    ### Does this PR introduce _any_ user-facing change?
    
    Yes, the `Environment`, `Log`, and `Back to Master` links work under a UI root. There is no change when the UI root is empty (default).
    
    ### How was this patch tested?
    
    Pass the CIs with the updated `MasterWorkerUISuite`, which fails without this fix. `MasterSuiteBase.verifyStaticResourcesServedByProxy` now checks all site-relative links, not only `/static`, and the test applies it to the Master `Environment` and `Log` pages too.
    
    ### Was this patch authored or co-authored using generative AI tooling?
    
    Generated-by: Claude Opus 5.5
    
    Closes #59595 from dongjoon-hyun/SPARK-60342.
    
    Authored-by: Dongjoon Hyun <do...@apache.org>
    Signed-off-by: Dongjoon Hyun <do...@apache.org>
    (cherry picked from commit 269a4f743045b7f04d2f4bce2e27b917dd1534e2)
    Signed-off-by: Dongjoon Hyun <do...@apache.org>
---
 .../scala/org/apache/spark/deploy/master/ui/EnvironmentPage.scala  | 2 +-
 .../src/main/scala/org/apache/spark/deploy/master/ui/LogPage.scala | 2 +-
 .../main/scala/org/apache/spark/deploy/master/ui/MasterPage.scala  | 4 ++--
 .../scala/org/apache/spark/deploy/master/MasterSuiteBase.scala     | 6 ++++++
 .../scala/org/apache/spark/deploy/master/MasterWorkerUISuite.scala | 7 +++++++
 5 files changed, 17 insertions(+), 4 deletions(-)

diff --git a/core/src/main/scala/org/apache/spark/deploy/master/ui/EnvironmentPage.scala b/core/src/main/scala/org/apache/spark/deploy/master/ui/EnvironmentPage.scala
index 087453175257..06a65d3641e5 100644
--- a/core/src/main/scala/org/apache/spark/deploy/master/ui/EnvironmentPage.scala
+++ b/core/src/main/scala/org/apache/spark/deploy/master/ui/EnvironmentPage.scala
@@ -62,7 +62,7 @@ private[ui] class EnvironmentPage(
 
     val content =
       <div>
-        <p><a href="/">Back to Master</a></p>
+        <p><a href={UIUtils.prependBaseUri(request, "/")}>Back to Master</a></p>
       </div>
       <span>
         <span class="collapse-table" data-bs-toggle="collapse"
diff --git a/core/src/main/scala/org/apache/spark/deploy/master/ui/LogPage.scala b/core/src/main/scala/org/apache/spark/deploy/master/ui/LogPage.scala
index 7da873622202..74227a5dfca9 100644
--- a/core/src/main/scala/org/apache/spark/deploy/master/ui/LogPage.scala
+++ b/core/src/main/scala/org/apache/spark/deploy/master/ui/LogPage.scala
@@ -62,7 +62,7 @@ private[ui] class LogPage(parent: MasterWebUI) extends WebUIPage("logPage") with
     val content =
       <script type="module" src={UIUtils.prependBaseUri(request, "/static/utils.js")}></script> ++
       <div>
-        <p><a href="/">Back to Master</a></p>
+        <p><a href={UIUtils.prependBaseUri(request, "/")}>Back to Master</a></p>
         {range}
         <div class="log-content overflow-auto p-1" style="height:80vh;">
           <div>{moreButton}</div>
diff --git a/core/src/main/scala/org/apache/spark/deploy/master/ui/MasterPage.scala b/core/src/main/scala/org/apache/spark/deploy/master/ui/MasterPage.scala
index c89f586f7e6d..68cabe63ecf8 100644
--- a/core/src/main/scala/org/apache/spark/deploy/master/ui/MasterPage.scala
+++ b/core/src/main/scala/org/apache/spark/deploy/master/ui/MasterPage.scala
@@ -193,8 +193,8 @@ private[ui] class MasterPage(parent: MasterWebUI) extends WebUIPage("") {
                 {state.completedDrivers.count(_.state == DriverState.RELAUNCHING)} Relaunching)
               </li>
               <li><strong>Status:</strong> {state.status}
-                (<a href={"/environment/"}>Environment</a>,
-                <a href={"/logPage/?self&logType=out"}>Log</a>)
+                (<a href={UIUtils.prependBaseUri(request, "/environment/")}>Environment</a>,
+                <a href={UIUtils.prependBaseUri(request, "/logPage/?self&logType=out")}>Log</a>)
               </li>
             </ul>
           </div>
diff --git a/core/src/test/scala/org/apache/spark/deploy/master/MasterSuiteBase.scala b/core/src/test/scala/org/apache/spark/deploy/master/MasterSuiteBase.scala
index 70291625cdb4..800ce81657e3 100644
--- a/core/src/test/scala/org/apache/spark/deploy/master/MasterSuiteBase.scala
+++ b/core/src/test/scala/org/apache/spark/deploy/master/MasterSuiteBase.scala
@@ -144,6 +144,9 @@ trait MasterSuiteBase extends SparkFunSuite
   // groups represent URL and worker ID
   val WORKER_LINK_RE = """<a href="(.+?)">\s*(worker-.+?)\s*</a>""".r
 
+  // regex to extract site-relative links from the webui HTML
+  val SITE_RELATIVE_LINK_RE = """href="(/[^"]*)"""".r
+
   private var _master: Master = _
 
   after {
@@ -179,6 +182,9 @@ trait MasterSuiteBase extends SparkFunSuite
     html should include (s"""href="$proxyUrl/static""")
     html should not include ("""src="/static""")
     html should include (s"""src="$proxyUrl/static""")
+    // other site-relative links, e.g. "Back to Master", must not bypass the proxy either
+    (SITE_RELATIVE_LINK_RE findAllMatchIn html).map(_.group(1))
+      .filterNot(_.startsWith(proxyUrl)).toSeq shouldBe empty
   }
 
   protected def verifyDrivers(
diff --git a/core/src/test/scala/org/apache/spark/deploy/master/MasterWorkerUISuite.scala b/core/src/test/scala/org/apache/spark/deploy/master/MasterWorkerUISuite.scala
index 5f206f611fe6..47ecdb3dd044 100644
--- a/core/src/test/scala/org/apache/spark/deploy/master/MasterWorkerUISuite.scala
+++ b/core/src/test/scala/org/apache/spark/deploy/master/MasterWorkerUISuite.scala
@@ -133,6 +133,13 @@ class MasterWorkerUISuite extends MasterSuiteBase {
           .tryWithResource(Source.fromURL(s"$masterUrl/"))(_.getLines().mkString("\n"))
         html should include ("Spark Master at spark://")
         verifyStaticResourcesServedByProxy(html, reverseProxyUrl)
+        Seq("environment/", "logPage/?self&logType=out").foreach { path =>
+          val pageHtml = Utils
+            .tryWithResource(Source.fromURL(s"$masterUrl/$path"))(_.getLines().mkString("\n"))
+          withClue(s"$path: ") {
+            verifyStaticResourcesServedByProxy(pageHtml, reverseProxyUrl)
+          }
+        }
         verifyWorkerUI(html, masterUrl, reverseProxyUrl)
       }
     } finally {


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