Skip to content

Commit 6918f0b

Browse files
committed
Added UIRoot redirection from Master to Application
1 parent 21cb506 commit 6918f0b

File tree

2 files changed

+90
-0
lines changed

2 files changed

+90
-0
lines changed

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

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,15 @@
1717

1818
package org.apache.spark.deploy.master.ui
1919

20+
import java.net.URL
21+
import java.util.regex.Pattern
22+
import javax.servlet.http.{HttpServlet, HttpServletRequest, HttpServletResponse}
23+
24+
import org.eclipse.jetty.servlet.ServletContextHandler
25+
2026
import org.apache.spark.Logging
2127
import org.apache.spark.deploy.master.Master
28+
import org.apache.spark.status.api.v1.{ApplicationsListResource, ApplicationInfo}
2229
import org.apache.spark.ui.{SparkUI, WebUI}
2330
import org.apache.spark.ui.JettyUtils._
2431

@@ -50,6 +57,66 @@ class MasterWebUI(
5057
"/app/kill", "/", masterPage.handleAppKillRequest, httpMethods = Set("POST")))
5158
attachHandler(createRedirectHandler(
5259
"/driver/kill", "/", masterPage.handleDriverKillRequest, httpMethods = Set("POST")))
60+
attachHandler(createApiRootHandler())
61+
}
62+
63+
def createApiRootHandler(): ServletContextHandler = {
64+
65+
val servlet = new HttpServlet {
66+
private lazy val appIdPattern = Pattern.compile("\\/api\\/v\\d+\\/applications\\/([^\\/]+).*")
67+
68+
override def doGet(request: HttpServletRequest, response: HttpServletResponse): Unit = {
69+
doRequest(request, response)
70+
}
71+
override def doPost(request: HttpServletRequest, response: HttpServletResponse): Unit = {
72+
doRequest(request, response)
73+
}
74+
private def doRequest(request: HttpServletRequest, response: HttpServletResponse): Unit = {
75+
val requestURI = request.getRequestURI
76+
77+
// requesting an application info list
78+
if (requestURI == "applications") {
79+
// TODO - Should send ApplicationList response ???
80+
} else {
81+
// forward request to app if it is active, otherwise send error
82+
getAppId(request) match {
83+
case Some(appId) =>
84+
val state = masterPage.getMasterState
85+
state.activeApps.find(appInfo => appInfo.id == appId) match {
86+
case Some(appInfo) =>
87+
val prefixedDestPath = appInfo.desc.appUiUrl + requestURI
88+
val newUrl = new URL(new URL(request.getRequestURL.toString), prefixedDestPath).toString
89+
response.sendRedirect(newUrl)
90+
request.getPathInfo
91+
case None =>
92+
response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE)
93+
}
94+
case None =>
95+
response.sendError(HttpServletResponse.SC_BAD_REQUEST)
96+
}
97+
}
98+
}
99+
100+
private def getAppId(request: HttpServletRequest): Option[String] = {
101+
val m = appIdPattern.matcher(request.getRequestURI)
102+
if (m.matches) Some(m.group(1)) else None
103+
}
104+
105+
// SPARK-5983 ensure TRACE is not supported
106+
protected override def doTrace(req: HttpServletRequest, res: HttpServletResponse): Unit = {
107+
res.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED)
108+
}
109+
}
110+
111+
createServletHandler("/api", servlet, "")
112+
}
113+
114+
def getApplicationInfoList: Iterator[ApplicationInfo] = {
115+
val state = masterPage.getMasterState
116+
val activeApps = state.activeApps.sortBy(_.startTime).reverse
117+
val completedApps = state.completedApps.sortBy(_.endTime).reverse
118+
activeApps.iterator.map { ApplicationsListResource.convertApplicationInfo(_, false) } ++
119+
completedApps.iterator.map { ApplicationsListResource.convertApplicationInfo(_, true) }
53120
}
54121
}
55122

core/src/main/scala/org/apache/spark/status/api/v1/ApplicationListResource.scala

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import javax.ws.rs.{DefaultValue, GET, Produces, QueryParam}
2121
import javax.ws.rs.core.MediaType
2222

2323
import org.apache.spark.deploy.history.ApplicationHistoryInfo
24+
import org.apache.spark.deploy.master.{ApplicationInfo => InternalApplicationInfo}
2425

2526
@Produces(Array(MediaType.APPLICATION_JSON))
2627
private[v1] class ApplicationListResource(uiRoot: UIRoot) {
@@ -76,4 +77,26 @@ private[spark] object ApplicationsListResource {
7677
}
7778
)
7879
}
80+
81+
def convertApplicationInfo(
82+
internal: InternalApplicationInfo,
83+
completed: Boolean): ApplicationInfo = {
84+
// standalone application info always has just one attempt
85+
new ApplicationInfo(
86+
id = internal.id,
87+
name = internal.desc.name,
88+
coresGranted = Some(internal.coresGranted),
89+
maxCores = internal.desc.maxCores,
90+
coresPerExecutor = internal.desc.coresPerExecutor,
91+
memoryPerExecutorMB = Some(internal.desc.memoryPerExecutorMB),
92+
attempts = Seq(new ApplicationAttemptInfo(
93+
attemptId = None,
94+
startTime = new Date(internal.startTime),
95+
endTime = new Date(internal.endTime),
96+
sparkUser = internal.desc.user,
97+
completed = completed
98+
))
99+
)
100+
}
101+
79102
}

0 commit comments

Comments
 (0)