MAPREDUCE-6787. Allow job_conf.xml to be downloadable on the job overview page in JHS (haibochen via rkanter)
This commit is contained in:
parent
2d77dc727d
commit
c87b3a448a
@ -323,6 +323,40 @@ public void conf() {
|
|||||||
render(confPage());
|
render(confPage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle requests to download the job configuration.
|
||||||
|
*/
|
||||||
|
public void downloadConf() {
|
||||||
|
try {
|
||||||
|
requireJob();
|
||||||
|
} catch (Exception e) {
|
||||||
|
renderText(e.getMessage());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
writeJobConf();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeJobConf() {
|
||||||
|
String jobId = $(JOB_ID);
|
||||||
|
assert(!jobId.isEmpty());
|
||||||
|
|
||||||
|
JobId jobID = MRApps.toJobID($(JOB_ID));
|
||||||
|
Job job = app.context.getJob(jobID);
|
||||||
|
assert(job != null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Configuration jobConf = job.loadConfFile();
|
||||||
|
response().setContentType("text/xml");
|
||||||
|
response().setHeader("Content-Disposition",
|
||||||
|
"attachment; filename=" + jobId + ".xml");
|
||||||
|
jobConf.writeXml(writer());
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.error("Error reading/writing job" +
|
||||||
|
" conf file for job: " + jobId, e);
|
||||||
|
renderText(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Render a BAD_REQUEST error.
|
* Render a BAD_REQUEST error.
|
||||||
* @param s the error message to include.
|
* @param s the error message to include.
|
||||||
|
@ -70,7 +70,7 @@ public class ConfBlock extends HtmlBlock {
|
|||||||
try {
|
try {
|
||||||
ConfInfo info = new ConfInfo(job);
|
ConfInfo info = new ConfInfo(job);
|
||||||
|
|
||||||
html.div().h3(confPath.toString())._();
|
html.div().a("/jobhistory/downloadconf/" + jid, confPath.toString());
|
||||||
TBODY<TABLE<Hamlet>> tbody = html.
|
TBODY<TABLE<Hamlet>> tbody = html.
|
||||||
// Tasks table
|
// Tasks table
|
||||||
table("#conf").
|
table("#conf").
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
|
import org.apache.hadoop.fs.Path;
|
||||||
import org.apache.hadoop.mapreduce.JobACL;
|
import org.apache.hadoop.mapreduce.JobACL;
|
||||||
import org.apache.hadoop.mapreduce.v2.api.records.JobId;
|
import org.apache.hadoop.mapreduce.v2.api.records.JobId;
|
||||||
import org.apache.hadoop.mapreduce.v2.api.records.TaskId;
|
import org.apache.hadoop.mapreduce.v2.api.records.TaskId;
|
||||||
@ -59,6 +60,8 @@ public void setUp() throws IOException {
|
|||||||
Task task = mock(Task.class);
|
Task task = mock(Task.class);
|
||||||
|
|
||||||
when(job.getTask(any(TaskId.class))).thenReturn(task);
|
when(job.getTask(any(TaskId.class))).thenReturn(task);
|
||||||
|
when(job.loadConfFile()).thenReturn(new Configuration());
|
||||||
|
when(job.getConfFile()).thenReturn(new Path("/"));
|
||||||
|
|
||||||
JobId jobID = MRApps.toJobID("job_01_01");
|
JobId jobID = MRApps.toJobID("job_01_01");
|
||||||
when(context.getJob(jobID)).thenReturn(job);
|
when(context.getJob(jobID)).thenReturn(job);
|
||||||
@ -265,6 +268,17 @@ public void testConfiguration() {
|
|||||||
assertEquals(JobConfPage.class, appController.getClazz());
|
assertEquals(JobConfPage.class, appController.getClazz());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test downloadConf request handling.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testDownloadConfiguration() {
|
||||||
|
appController.downloadConf();
|
||||||
|
String jobConfXml = appController.getData();
|
||||||
|
assertTrue("Error downloading the job configuration file.",
|
||||||
|
!jobConfXml.contains("Error"));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test method 'conf'. Should set AttemptsPage class for rendering or print information about error
|
* Test method 'conf'. Should set AttemptsPage class for rendering or print information about error
|
||||||
*/
|
*/
|
||||||
|
@ -50,6 +50,8 @@ public void setup() {
|
|||||||
route("/app", HsController.class);
|
route("/app", HsController.class);
|
||||||
route(pajoin("/job", JOB_ID), HsController.class, "job");
|
route(pajoin("/job", JOB_ID), HsController.class, "job");
|
||||||
route(pajoin("/conf", JOB_ID), HsController.class, "conf");
|
route(pajoin("/conf", JOB_ID), HsController.class, "conf");
|
||||||
|
routeWithoutDefaultView(pajoin("/downloadconf", JOB_ID),
|
||||||
|
HsController.class, "downloadConf");
|
||||||
route(pajoin("/jobcounters", JOB_ID), HsController.class, "jobCounters");
|
route(pajoin("/jobcounters", JOB_ID), HsController.class, "jobCounters");
|
||||||
route(pajoin("/singlejobcounter",JOB_ID, COUNTER_GROUP, COUNTER_NAME),
|
route(pajoin("/singlejobcounter",JOB_ID, COUNTER_GROUP, COUNTER_NAME),
|
||||||
HsController.class, "singleJobCounter");
|
HsController.class, "singleJobCounter");
|
||||||
|
@ -74,17 +74,32 @@ static class Dest {
|
|||||||
|
|
||||||
final TreeMap<String, Dest> routes = Maps.newTreeMap(); // path->dest
|
final TreeMap<String, Dest> routes = Maps.newTreeMap(); // path->dest
|
||||||
|
|
||||||
|
synchronized Dest add(WebApp.HTTP httpMethod, String path,
|
||||||
|
Class<? extends Controller> cls,
|
||||||
|
String action, List<String> names){
|
||||||
|
return addWithOptionalDefaultView(
|
||||||
|
httpMethod, path, cls, action, names, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized Dest addWithoutDefaultView(WebApp.HTTP httpMethod,
|
||||||
|
String path, Class<? extends Controller> cls, String action,
|
||||||
|
List<String> names){
|
||||||
|
return addWithOptionalDefaultView(httpMethod, path, cls, action,
|
||||||
|
names, false);
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Add a route to the router.
|
* Add a route to the router.
|
||||||
* e.g., add(GET, "/foo/show", FooController.class, "show", [name...]);
|
* e.g., add(GET, "/foo/show", FooController.class, "show", [name...]);
|
||||||
* The name list is from /foo/show/:name/...
|
* The name list is from /foo/show/:name/...
|
||||||
*/
|
*/
|
||||||
synchronized Dest add(WebApp.HTTP httpMethod, String path,
|
synchronized Dest addWithOptionalDefaultView(WebApp.HTTP httpMethod,
|
||||||
Class<? extends Controller> cls,
|
String path, Class<? extends Controller> cls,
|
||||||
String action, List<String> names) {
|
String action, List<String> names, boolean defaultViewNeeded) {
|
||||||
LOG.debug("adding {}({})->{}#{}", new Object[]{path, names, cls, action});
|
LOG.debug("adding {}({})->{}#{}", new Object[]{path, names, cls, action});
|
||||||
Dest dest = addController(httpMethod, path, cls, action, names);
|
Dest dest = addController(httpMethod, path, cls, action, names);
|
||||||
|
if (defaultViewNeeded) {
|
||||||
addDefaultView(dest);
|
addDefaultView(dest);
|
||||||
|
}
|
||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,6 +210,19 @@ public void route(HTTP method, String pathSpec,
|
|||||||
res.subList(R_PARAMS, res.size()));
|
res.subList(R_PARAMS, res.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup of a webapp serving route without default views added to the page.
|
||||||
|
* @param pathSpec the path spec in the form of /controller/action/:args etc.
|
||||||
|
* @param cls the controller class
|
||||||
|
* @param action the controller method
|
||||||
|
*/
|
||||||
|
public void routeWithoutDefaultView(String pathSpec,
|
||||||
|
Class<? extends Controller> cls, String action) {
|
||||||
|
List<String> res = parseRoute(pathSpec);
|
||||||
|
router.addWithoutDefaultView(HTTP.GET, res.get(R_PATH), cls, action,
|
||||||
|
res.subList(R_PARAMS, res.size()));
|
||||||
|
}
|
||||||
|
|
||||||
public void route(String pathSpec, Class<? extends Controller> cls,
|
public void route(String pathSpec, Class<? extends Controller> cls,
|
||||||
String action) {
|
String action) {
|
||||||
route(HTTP.GET, pathSpec, cls, action);
|
route(HTTP.GET, pathSpec, cls, action);
|
||||||
|
Loading…
Reference in New Issue
Block a user