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:54:00 UTC
(spark) branch branch-4.1 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.1
in repository https://gitbox.apache.org/repos/asf/spark.git
The following commit(s) were added to refs/heads/branch-4.1 by this push:
new 6b1ca4d0e7b0 [SPARK-60342][CORE][UI] Respect UI root in Master UI links
6b1ca4d0e7b0 is described below
commit 6b1ca4d0e7b0c62bbd562cd9687d88965f363dd9
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 977f8cfae75e..9ee813500218 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-aggregated-runtimeInformation collapse-table"
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 9b5c5e46d477..efd4ba0fa4fe 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" style="height:80vh; overflow:auto; padding:5px;">
<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 c5313a76d78f..2dd00a23deb9 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
@@ -172,8 +172,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