Skip to content

Commit 34d2134

Browse files
aosagiesrowen
authored andcommitted
[SPARK-21176][WEB UI] Format worker page links to work with proxy
## What changes were proposed in this pull request? Several links on the worker page do not work correctly with the proxy because: 1) They don't acknowledge the proxy 2) They use relative paths (unlike the Application Page which uses full paths) This patch fixes that. It also fixes a mistake in the proxy's Location header parsing which caused it to incorrectly handle redirects. ## How was this patch tested? I checked the validity of every link with the proxy on and off. Author: Anderson Osagie <[email protected]> Closes apache#18915 from aosagie/fix/proxy-links.
1 parent 5596ce8 commit 34d2134

File tree

5 files changed

+32
-19
lines changed

5 files changed

+32
-19
lines changed

core/src/main/scala/org/apache/spark/deploy/master/ui/ApplicationPage.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,10 @@ private[ui] class ApplicationPage(parent: MasterWebUI) extends WebUIPage("app")
125125
<td>{executor.memory}</td>
126126
<td>{executor.state}</td>
127127
<td>
128-
<a href={"%s/logPage?appId=%s&executorId=%s&logType=stdout"
129-
.format(workerUrlRef, executor.application.id, executor.id)}>stdout</a>
130-
<a href={"%s/logPage?appId=%s&executorId=%s&logType=stderr"
131-
.format(workerUrlRef, executor.application.id, executor.id)}>stderr</a>
128+
<a href={s"$workerUrlRef/logPage?appId=${executor.application.id}&executorId=${executor.
129+
id}&logType=stdout"}>stdout</a>
130+
<a href={s"$workerUrlRef/logPage?appId=${executor.application.id}&executorId=${executor.
131+
id}&logType=stderr"}>stderr</a>
132132
</td>
133133
</tr>
134134
}

core/src/main/scala/org/apache/spark/deploy/worker/Worker.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ private[deploy] class Worker(
155155
private val metricsSystem = MetricsSystem.createMetricsSystem("worker", conf, securityMgr)
156156
private val workerSource = new WorkerSource(this)
157157

158+
val reverseProxy = conf.getBoolean("spark.ui.reverseProxy", false)
159+
158160
private var registerMasterFutures: Array[JFuture[_]] = null
159161
private var registrationRetryTimer: Option[JScheduledFuture[_]] = None
160162

@@ -225,7 +227,7 @@ private[deploy] class Worker(
225227
masterAddressToConnect = Some(masterAddress)
226228
master = Some(masterRef)
227229
connected = true
228-
if (conf.getBoolean("spark.ui.reverseProxy", false)) {
230+
if (reverseProxy) {
229231
logInfo(s"WorkerWebUI is available at $activeMasterWebUiUrl/proxy/$workerId")
230232
}
231233
// Cancel any outstanding re-registration attempts because we found a new master

core/src/main/scala/org/apache/spark/deploy/worker/ui/WorkerPage.scala

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,11 @@ private[ui] class WorkerPage(parent: WorkerWebUI) extends WebUIPage("") {
5151

5252
val driverHeaders = Seq("DriverID", "Main Class", "State", "Cores", "Memory", "Logs", "Notes")
5353
val runningDrivers = workerState.drivers.sortBy(_.driverId).reverse
54-
val runningDriverTable = UIUtils.listingTable(driverHeaders, driverRow, runningDrivers)
54+
val runningDriverTable = UIUtils.listingTable[DriverRunner](driverHeaders,
55+
driverRow(workerState.workerId, _), runningDrivers)
5556
val finishedDrivers = workerState.finishedDrivers.sortBy(_.driverId).reverse
56-
val finishedDriverTable = UIUtils.listingTable(driverHeaders, driverRow, finishedDrivers)
57+
val finishedDriverTable = UIUtils.listingTable[DriverRunner](driverHeaders,
58+
driverRow(workerState.workerId, _), finishedDrivers)
5759

5860
// For now we only show driver information if the user has submitted drivers to the cluster.
5961
// This is until we integrate the notion of drivers and applications in the UI.
@@ -102,6 +104,11 @@ private[ui] class WorkerPage(parent: WorkerWebUI) extends WebUIPage("") {
102104
}
103105

104106
def executorRow(executor: ExecutorRunner): Seq[Node] = {
107+
val workerUrlRef = UIUtils.makeHref(parent.worker.reverseProxy, executor.workerId,
108+
parent.webUrl)
109+
val appUrlRef = UIUtils.makeHref(parent.worker.reverseProxy, executor.appId,
110+
executor.appDesc.appUiUrl)
111+
105112
<tr>
106113
<td>{executor.execId}</td>
107114
<td>{executor.cores}</td>
@@ -115,7 +122,7 @@ private[ui] class WorkerPage(parent: WorkerWebUI) extends WebUIPage("") {
115122
<li><strong>Name:</strong>
116123
{
117124
if ({executor.state == ExecutorState.RUNNING} && executor.appDesc.appUiUrl.nonEmpty) {
118-
<a href={executor.appDesc.appUiUrl}> {executor.appDesc.name}</a>
125+
<a href={appUrlRef}> {executor.appDesc.name}</a>
119126
} else {
120127
{executor.appDesc.name}
121128
}
@@ -125,16 +132,17 @@ private[ui] class WorkerPage(parent: WorkerWebUI) extends WebUIPage("") {
125132
</ul>
126133
</td>
127134
<td>
128-
<a href={"logPage?appId=%s&executorId=%s&logType=stdout"
129-
.format(executor.appId, executor.execId)}>stdout</a>
130-
<a href={"logPage?appId=%s&executorId=%s&logType=stderr"
131-
.format(executor.appId, executor.execId)}>stderr</a>
135+
<a href={s"$workerUrlRef/logPage?appId=${executor
136+
.appId}&executorId=${executor.execId}&logType=stdout"}>stdout</a>
137+
<a href={s"$workerUrlRef/logPage?appId=${executor
138+
.appId}&executorId=${executor.execId}&logType=stderr"}>stderr</a>
132139
</td>
133140
</tr>
134141

135142
}
136143

137-
def driverRow(driver: DriverRunner): Seq[Node] = {
144+
def driverRow(workerId: String, driver: DriverRunner): Seq[Node] = {
145+
val workerUrlRef = UIUtils.makeHref(parent.worker.reverseProxy, workerId, parent.webUrl)
138146
<tr>
139147
<td>{driver.driverId}</td>
140148
<td>{driver.driverDesc.command.arguments(2)}</td>
@@ -146,8 +154,8 @@ private[ui] class WorkerPage(parent: WorkerWebUI) extends WebUIPage("") {
146154
{Utils.megabytesToString(driver.driverDesc.mem)}
147155
</td>
148156
<td>
149-
<a href={s"logPage?driverId=${driver.driverId}&logType=stdout"}>stdout</a>
150-
<a href={s"logPage?driverId=${driver.driverId}&logType=stderr"}>stderr</a>
157+
<a href={s"$workerUrlRef/logPage?driverId=${driver.driverId}&logType=stdout"}>stdout</a>
158+
<a href={s"$workerUrlRef/logPage?driverId=${driver.driverId}&logType=stderr"}>stderr</a>
151159
</td>
152160
<td>
153161
{driver.finalException.getOrElse("")}

core/src/main/scala/org/apache/spark/ui/JettyUtils.scala

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,8 @@ private[spark] object JettyUtils extends Logging {
209209
val id = prefix.drop(1)
210210

211211
// Query master state for id's corresponding UI address
212-
// If that address exists, turn it into a valid, target URI string or return null
212+
// If that address exists, try to turn it into a valid, target URI string
213+
// Otherwise, return null
213214
idToUiAddress(id)
214215
.map(createProxyURI(prefix, _, path, request.getQueryString))
215216
.filter(uri => uri != null && validateDestination(uri.getHost, uri.getPort))
@@ -467,8 +468,10 @@ private[spark] object JettyUtils extends Logging {
467468
targetUri: URI): String = {
468469
val toReplace = targetUri.getScheme() + "://" + targetUri.getAuthority()
469470
if (headerValue.startsWith(toReplace)) {
470-
clientRequest.getScheme() + "://" + clientRequest.getHeader("host") +
471-
clientRequest.getPathInfo() + headerValue.substring(toReplace.length())
471+
val id = clientRequest.getPathInfo.substring("/proxy/".length).takeWhile(_ != '/')
472+
val headerPath = headerValue.substring(toReplace.length)
473+
474+
s"${clientRequest.getScheme}://${clientRequest.getHeader("host")}/proxy/$id$headerPath"
472475
} else {
473476
null
474477
}

core/src/test/scala/org/apache/spark/ui/UISuite.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ class UISuite extends SparkFunSuite {
223223
val targetUri = URI.create("http://localhost:4040")
224224
when(clientRequest.getScheme()).thenReturn("http")
225225
when(clientRequest.getHeader("host")).thenReturn("localhost:8080")
226-
when(clientRequest.getPathInfo()).thenReturn("/proxy/worker-id")
226+
when(clientRequest.getPathInfo()).thenReturn("/proxy/worker-id/jobs")
227227
var newHeader = JettyUtils.createProxyLocationHeader(headerValue, clientRequest, targetUri)
228228
assert(newHeader.toString() === "http://localhost:8080/proxy/worker-id/jobs")
229229
headerValue = "http://localhost:4041/jobs"

0 commit comments

Comments
 (0)