Skip to content

Commit 32cca93

Browse files
yoshidakuysarutak
authored andcommitted
[SPARK-12708][UI] Sorting task error in Stages Page when yarn mode.
If sort column contains slash(e.g. "Executor ID / Host") when yarn mode,sort fail with following message. ![spark-12708](https://cloud.githubusercontent.com/assets/6679275/12193320/80814f8c-b62a-11e5-9914-7bf3907029df.png) It's similar to SPARK-4313 . Author: root <root@R520T1.(none)> Author: Koyo Yoshida <koyo0615@gmail.com> Closes apache#10663 from yoshidakuy/SPARK-12708.
1 parent cc7af86 commit 32cca93

File tree

6 files changed

+46
-18
lines changed

6 files changed

+46
-18
lines changed

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
package org.apache.spark.ui
1919

20+
import java.net.URLDecoder
2021
import java.text.SimpleDateFormat
2122
import java.util.{Date, Locale}
2223

@@ -451,4 +452,19 @@ private[spark] object UIUtils extends Logging {
451452
<span class="description-input">{desc}</span>
452453
}
453454
}
455+
456+
/**
457+
* Decode URLParameter if URL is encoded by YARN-WebAppProxyServlet.
458+
* Due to YARN-2844: WebAppProxyServlet cannot handle urls which contain encoded characters
459+
* Therefore we need to decode it until we get the real URLParameter.
460+
*/
461+
def decodeURLParameter(urlParam: String): String = {
462+
var param = urlParam
463+
var decodedParam = URLDecoder.decode(param, "UTF-8")
464+
while (param != decodedParam) {
465+
param = decodedParam
466+
decodedParam = URLDecoder.decode(param, "UTF-8")
467+
}
468+
param
469+
}
454470
}

core/src/main/scala/org/apache/spark/ui/exec/ExecutorThreadDumpPage.scala

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
package org.apache.spark.ui.exec
1919

20-
import java.net.URLDecoder
2120
import javax.servlet.http.HttpServletRequest
2221

2322
import scala.util.Try
@@ -30,18 +29,8 @@ private[ui] class ExecutorThreadDumpPage(parent: ExecutorsTab) extends WebUIPage
3029
private val sc = parent.sc
3130

3231
def render(request: HttpServletRequest): Seq[Node] = {
33-
val executorId = Option(request.getParameter("executorId")).map {
34-
executorId =>
35-
// Due to YARN-2844, "<driver>" in the url will be encoded to "%25253Cdriver%25253E" when
36-
// running in yarn-cluster mode. `request.getParameter("executorId")` will return
37-
// "%253Cdriver%253E". Therefore we need to decode it until we get the real id.
38-
var id = executorId
39-
var decodedId = URLDecoder.decode(id, "UTF-8")
40-
while (id != decodedId) {
41-
id = decodedId
42-
decodedId = URLDecoder.decode(id, "UTF-8")
43-
}
44-
id
32+
val executorId = Option(request.getParameter("executorId")).map { executorId =>
33+
UIUtils.decodeURLParameter(executorId)
4534
}.getOrElse {
4635
throw new IllegalArgumentException(s"Missing executorId parameter")
4736
}

core/src/main/scala/org/apache/spark/ui/jobs/PoolPage.scala

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,11 @@ private[ui] class PoolPage(parent: StagesTab) extends WebUIPage("pool") {
3131

3232
def render(request: HttpServletRequest): Seq[Node] = {
3333
listener.synchronized {
34-
val poolName = request.getParameter("poolname")
35-
require(poolName != null && poolName.nonEmpty, "Missing poolname parameter")
34+
val poolName = Option(request.getParameter("poolname")).map { poolname =>
35+
UIUtils.decodeURLParameter(poolname)
36+
}.getOrElse {
37+
throw new IllegalArgumentException(s"Missing poolname parameter")
38+
}
3639

3740
val poolToActiveStages = listener.poolToActiveStages
3841
val activeStages = poolToActiveStages.get(poolName) match {
@@ -44,7 +47,9 @@ private[ui] class PoolPage(parent: StagesTab) extends WebUIPage("pool") {
4447
killEnabled = parent.killEnabled)
4548

4649
// For now, pool information is only accessible in live UIs
47-
val pools = sc.map(_.getPoolForName(poolName).get).toSeq
50+
val pools = sc.map(_.getPoolForName(poolName).getOrElse {
51+
throw new IllegalArgumentException(s"Unknown poolname: $poolName")
52+
}).toSeq
4853
val poolTable = new PoolTable(pools, parent)
4954

5055
val content =

core/src/main/scala/org/apache/spark/ui/jobs/PoolTable.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
package org.apache.spark.ui.jobs
1919

20+
import java.net.URLEncoder
21+
2022
import scala.collection.mutable.HashMap
2123
import scala.xml.Node
2224

@@ -59,7 +61,7 @@ private[ui] class PoolTable(pools: Seq[Schedulable], parent: StagesTab) {
5961
case None => 0
6062
}
6163
val href = "%s/stages/pool?poolname=%s"
62-
.format(UIUtils.prependBaseUri(parent.basePath), p.name)
64+
.format(UIUtils.prependBaseUri(parent.basePath), URLEncoder.encode(p.name, "UTF-8"))
6365
<tr>
6466
<td>
6567
<a href={href}>{p.name}</a>

core/src/main/scala/org/apache/spark/ui/jobs/StagePage.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,9 @@ private[ui] class StagePage(parent: StagesTab) extends WebUIPage("stage") {
100100
val parameterTaskPrevPageSize = request.getParameter("task.prevPageSize")
101101

102102
val taskPage = Option(parameterTaskPage).map(_.toInt).getOrElse(1)
103-
val taskSortColumn = Option(parameterTaskSortColumn).getOrElse("Index")
103+
val taskSortColumn = Option(parameterTaskSortColumn).map { sortColumn =>
104+
UIUtils.decodeURLParameter(sortColumn)
105+
}.getOrElse("Index")
104106
val taskSortDesc = Option(parameterTaskSortDesc).map(_.toBoolean).getOrElse(false)
105107
val taskPageSize = Option(parameterTaskPageSize).map(_.toInt).getOrElse(100)
106108
val taskPrevPageSize = Option(parameterTaskPrevPageSize).map(_.toInt).getOrElse(taskPageSize)

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,20 @@ class UIUtilsSuite extends SparkFunSuite {
6767
s"\nRunning progress bar should round down\n\nExpected:\n$expected\nGenerated:\n$generated")
6868
}
6969

70+
test("decodeURLParameter (SPARK-12708: Sorting task error in Stages Page when yarn mode.)") {
71+
val encoded1 = "%252F"
72+
val decoded1 = "/"
73+
val encoded2 = "%253Cdriver%253E"
74+
val decoded2 = "<driver>"
75+
76+
assert(decoded1 === decodeURLParameter(encoded1))
77+
assert(decoded2 === decodeURLParameter(encoded2))
78+
79+
// verify that no affect to decoded URL.
80+
assert(decoded1 === decodeURLParameter(decoded1))
81+
assert(decoded2 === decodeURLParameter(decoded2))
82+
}
83+
7084
private def verify(
7185
desc: String, expected: Elem, errorMsg: String = "", baseUrl: String = ""): Unit = {
7286
val generated = makeDescription(desc, baseUrl)

0 commit comments

Comments
 (0)