diff --git hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/main/java/org/apache/hadoop/mapred/ResourceMgrDelegate.java hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/main/java/org/apache/hadoop/mapred/ResourceMgrDelegate.java index 74b07c2..fb5117b 100644 --- hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/main/java/org/apache/hadoop/mapred/ResourceMgrDelegate.java +++ hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/main/java/org/apache/hadoop/mapred/ResourceMgrDelegate.java @@ -281,9 +281,15 @@ public YarnClientApplication createApplication() throws @Override public void killApplication(ApplicationId applicationId) throws YarnException, IOException { - client.killApplication(applicationId); + killApplication(applicationId, null); } + public void killApplication(ApplicationId applicationId, String reason) + throws YarnException, IOException { + client.killApplication(applicationId, reason); + } + + @Override public ApplicationReport getApplicationReport(ApplicationId appId) throws YarnException, IOException { diff --git hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapreduce/v2/TestMRJobs.java hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapreduce/v2/TestMRJobs.java index 1ca1fd4..a96e5e3 100644 --- hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapreduce/v2/TestMRJobs.java +++ hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/test/java/org/apache/hadoop/mapreduce/v2/TestMRJobs.java @@ -53,6 +53,7 @@ import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.io.NullWritable; import org.apache.hadoop.io.Text; +import org.apache.hadoop.mapred.ResourceMgrDelegate; import org.apache.hadoop.mapreduce.Counters; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.JobCounter; @@ -65,6 +66,7 @@ import org.apache.hadoop.mapreduce.TaskID; import org.apache.hadoop.mapreduce.TaskReport; import org.apache.hadoop.mapreduce.TaskType; +import org.apache.hadoop.mapreduce.TypeConverter; import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; import org.apache.hadoop.mapreduce.lib.output.FileOutputCommitter; import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; @@ -75,6 +77,8 @@ import org.apache.hadoop.security.token.TokenIdentifier; import org.apache.hadoop.util.JarFinder; import org.apache.hadoop.util.Shell; +import org.apache.hadoop.yarn.client.api.YarnClient; +import org.apache.hadoop.yarn.client.cli.ApplicationCLI; import org.apache.hadoop.yarn.conf.YarnConfiguration; import org.junit.AfterClass; import org.junit.Assert; @@ -190,6 +194,66 @@ public void testSleepJob() throws IOException, InterruptedException, // JobStatus?)--compare against MRJobConfig.JOB_UBERTASK_ENABLE value } + @Test (timeout = 300000) + public void testSleepAppKillWithReason() throws Exception { + LOG.info("\n\n\nStarting testSleepAppKillWithReason()."); + + if (!(new File(MiniMRYarnCluster.APPJAR)).exists()) { + LOG.info("MRAppJar " + MiniMRYarnCluster.APPJAR + + " not found. Not running test."); + return; + } + final ApplicationCLI appCli = new ApplicationCLI(); + final ResourceMgrDelegate client = + new ResourceMgrDelegate(new YarnConfiguration(mrCluster.getConfig())); + appCli.setClient(client); + appCli.setSysOutPrintStream(System.out); + + final Configuration sleepConf = new Configuration(mrCluster.getConfig()); + final SleepJob sleepJob = new SleepJob(); + sleepJob.setConf(sleepConf); + final Job job = sleepJob.createJob(1, 0, 10000, 10, 0, 0); + job.addFileToClassPath(APP_JAR); // The AppMaster jar itself. + job.setJarByClass(SleepJob.class); + job.setMaxMapAttempts(4); + job.submit(); + + // wait until running + // + TaskReport[] reports = new TaskReport[0]; + while ( reports.length == 0 + || reports[0].getRunningTaskAttemptIds().isEmpty()) { + Thread.sleep(1000); + reports = job.getTaskReports(TaskType.MAP); + } + + // kill application + // + final String killAppReason = "Kill reason for app"; + final String reasonTail = "Don't include this"; + final StringBuilder tooLongOfAReason = + new StringBuilder(YarnClient.MAX_KILL_REASON_LENGTH + 10); + tooLongOfAReason.append(killAppReason); + while (tooLongOfAReason.length() < YarnClient.MAX_KILL_REASON_LENGTH) { + tooLongOfAReason.append('!'); + } + tooLongOfAReason.append(reasonTail); + + int exitCode = appCli.run( + new String[] { + "-kill", + TypeConverter.toYarn(job.getJobID()).getAppId().toString(), + tooLongOfAReason.toString()}); + + Assert.assertSame(0, exitCode); + Assert.assertFalse("Job should fail", job.waitForCompletion(false)); + final String jobDiagnostics = job.getStatus().getFailureInfo(); + Assert.assertTrue("No job kill reason in: " + jobDiagnostics, + jobDiagnostics.contains("\"" + killAppReason + "!!!!")); + Assert.assertFalse("Extra characters in: " + jobDiagnostics, + jobDiagnostics.contains(reasonTail)); + } + protected void verifySleepJobCounters(Job job) throws InterruptedException, IOException { Counters counters = job.getCounters(); diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/KillApplicationRequest.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/KillApplicationRequest.java index 606cf4e..391b5b8 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/KillApplicationRequest.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/KillApplicationRequest.java @@ -57,4 +57,10 @@ public static KillApplicationRequest newInstance(ApplicationId applicationId) { @Public @Stable public abstract void setApplicationId(ApplicationId applicationId); + + @Public + public abstract String getReason(); + + @Public + public abstract void setReason(String reason); } diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/proto/yarn_service_protos.proto hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/proto/yarn_service_protos.proto index a4631d1..059a129 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/proto/yarn_service_protos.proto +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/proto/yarn_service_protos.proto @@ -113,6 +113,7 @@ message SubmitApplicationResponseProto { message KillApplicationRequestProto { optional ApplicationIdProto application_id = 1; + optional string reason = 2; } message KillApplicationResponseProto { diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/YarnClient.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/YarnClient.java index 155ba5d..80c5d2a 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/YarnClient.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/YarnClient.java @@ -46,6 +46,7 @@ @InterfaceAudience.Public @InterfaceStability.Stable public abstract class YarnClient extends AbstractService { + public static final int MAX_KILL_REASON_LENGTH = 128; /** * Create a new instance of YarnClient. @@ -101,12 +102,16 @@ public abstract ApplicationId submitApplication(ApplicationSubmissionContext app * * @param applicationId * {@link ApplicationId} of the application that needs to be killed + * @param reason justification for application kill * @throws YarnException * in case of errors or if YARN rejects the request due to * access-control restrictions. * @throws IOException * @see #getQueueAclsInfo() */ + public abstract void killApplication(ApplicationId applicationId, + String reason) throws YarnException, IOException; + public abstract void killApplication(ApplicationId applicationId) throws YarnException, IOException; diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/impl/YarnClientImpl.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/impl/YarnClientImpl.java index a5ff9f6..26e6c11 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/impl/YarnClientImpl.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/impl/YarnClientImpl.java @@ -179,9 +179,20 @@ public YarnClientApplication createApplication() @Override public void killApplication(ApplicationId applicationId) throws YarnException, IOException { + killApplication(applicationId, null); + } + + public void killApplication(ApplicationId applicationId, String reason) + throws YarnException, IOException { KillApplicationRequest request = Records.newRecord(KillApplicationRequest.class); request.setApplicationId(applicationId); + if (reason != null) { + if (reason.length() > YarnClient.MAX_KILL_REASON_LENGTH) { + reason = reason.substring(0, YarnClient.MAX_KILL_REASON_LENGTH); + } + } + request.setReason(reason); try { int pollCount = 0; diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/cli/ApplicationCLI.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/cli/ApplicationCLI.java index a7b7d65..beffab3 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/cli/ApplicationCLI.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/cli/ApplicationCLI.java @@ -74,7 +74,10 @@ public int run(String[] args) throws Exception { "Supports optional use of -appTypes to filter applications " + "based on application type, " + "and -appStates to filter applications based on application state"); - opts.addOption(KILL_CMD, true, "Kills the application."); + Option killOption = new Option(KILL_CMD, true, "Kills the application."); + killOption.setArgs(2); + killOption.setArgName("Application ID [reason]"); + opts.addOption(killOption); opts.addOption(HELP_CMD, false, "Displays help for all commands."); Option appTypeOpt = new Option(APP_TYPE_CMD, true, "Works with -list to " + "filter applications based on " + @@ -90,7 +93,6 @@ public int run(String[] args) throws Exception { appStateOpt.setArgs(Option.UNLIMITED_VALUES); appStateOpt.setArgName("States"); opts.addOption(appStateOpt); - opts.getOption(KILL_CMD).setArgName("Application ID"); opts.getOption(STATUS_CMD).setArgName("Application ID"); int exitCode = -1; @@ -149,11 +151,12 @@ public int run(String[] args) throws Exception { } listApplications(appTypes, appStates); } else if (cliParser.hasOption(KILL_CMD)) { - if (args.length != 2) { + if (args.length < 2 || args.length > 3) { printUsage(opts); return exitCode; } - killApplication(cliParser.getOptionValue(KILL_CMD)); + final String[] killArgs = cliParser.getOptionValues(KILL_CMD); + killApplication(killArgs[0], killArgs.length == 2 ? killArgs[1] : null); } else if (cliParser.hasOption(HELP_CMD)) { printUsage(opts); return 0; @@ -224,10 +227,11 @@ private void listApplications(Set appTypes, * Kills the application with the application id as appId * * @param applicationId + * @param reason * @throws YarnException * @throws IOException */ - private void killApplication(String applicationId) + private void killApplication(String applicationId, String reason) throws YarnException, IOException { ApplicationId appId = ConverterUtils.toApplicationId(applicationId); ApplicationReport appReport = client.getApplicationReport(appId); @@ -237,7 +241,7 @@ private void killApplication(String applicationId) sysout.println("Application " + applicationId + " has already finished "); } else { sysout.println("Killing application " + applicationId); - client.killApplication(appId); + client.killApplication(appId, reason); } } @@ -298,12 +302,12 @@ private String getAllValidApplicationStates() { StringBuilder sb = new StringBuilder(); sb.append("The valid application state can be" + " one of the following: "); - sb.append(ALLSTATES_OPTION + ","); - for (YarnApplicationState appState : YarnApplicationState - .values()) { - sb.append(appState+","); + sb.append(ALLSTATES_OPTION + ", "); + final YarnApplicationState[] appStates = YarnApplicationState.values(); + sb.append(appStates[0]); + for (int i = 1; i < appStates.length; i++) { + sb.append(", " + appStates[i]); } - String output = sb.toString(); - return output.substring(0, output.length()-1); + return sb.toString(); } } diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/cli/TestYarnCLI.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/cli/TestYarnCLI.java index 1d08f24..bc63761 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/cli/TestYarnCLI.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/test/java/org/apache/hadoop/yarn/client/cli/TestYarnCLI.java @@ -20,6 +20,7 @@ import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.isA; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; @@ -29,15 +30,16 @@ import static org.mockito.Mockito.doThrow; import java.io.ByteArrayOutputStream; -import java.io.IOException; import java.io.PrintStream; import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Date; import java.util.EnumSet; import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.regex.Pattern; import junit.framework.Assert; @@ -60,6 +62,10 @@ import org.apache.commons.cli.Options; public class TestYarnCLI { + private static final Pattern SPACES_PATTERN = + Pattern.compile("\\s+|\\n+|\\t+"); + private static final String APP_HELP = createApplicationCLIHelpMessage(); + private static final String NODE_HELP = createNodeCLIHelpMessage(); private YarnClient client = mock(YarnClient.class); ByteArrayOutputStream sysOutStream; @@ -366,12 +372,11 @@ public void testGetApplications() throws Exception { pw.println("The application state INVALID is invalid."); pw.print("The valid application state can be one of the following: "); StringBuilder sb = new StringBuilder(); - sb.append("ALL,"); + sb.append("ALL"); for(YarnApplicationState state : YarnApplicationState.values()) { - sb.append(state+","); + sb.append(", " + state); } - String output = sb.toString(); - pw.println(output.substring(0, output.length()-1)); + pw.println(sb.toString()); pw.close(); appsReportStr = baos.toString("UTF-8"); Assert.assertEquals(appsReportStr, sysOutStream.toString()); @@ -507,23 +512,21 @@ public void testAppsHelpCommand() throws Exception { int result = spyCli.run(new String[] { "-help" }); Assert.assertTrue(result == 0); verify(spyCli).printUsage(any(Options.class)); - Assert.assertEquals(createApplicationCLIHelpMessage(), - sysOutStream.toString()); - + Assert.assertEquals(APP_HELP, normalize(sysOutStream.toString())); sysOutStream.reset(); ApplicationId applicationId = ApplicationId.newInstance(1234, 5); - result = - cli.run(new String[] { "-kill", applicationId.toString(), "args" }); + result = cli.run(new String[] { + "-kill", applicationId.toString(), "test reason", "args" }); + Assert.assertSame("Exit code", -1, result); verify(spyCli).printUsage(any(Options.class)); - Assert.assertEquals(createApplicationCLIHelpMessage(), - sysOutStream.toString()); + Assert.assertEquals(APP_HELP, normalize(sysOutStream.toString())); sysOutStream.reset(); NodeId nodeId = NodeId.newInstance("host0", 0); result = cli.run(new String[] { "-status", nodeId.toString(), "args" }); + Assert.assertSame("Exit code", -1, result); verify(spyCli).printUsage(any(Options.class)); - Assert.assertEquals(createApplicationCLIHelpMessage(), - sysOutStream.toString()); + Assert.assertEquals(APP_HELP, normalize(sysOutStream.toString())); } @Test (timeout = 5000) @@ -533,8 +536,7 @@ public void testNodesHelpCommand() throws Exception { nodeCLI.setSysOutPrintStream(sysOut); nodeCLI.setSysErrPrintStream(sysErr); nodeCLI.run(new String[] {}); - Assert.assertEquals(createNodeCLIHelpMessage(), - sysOutStream.toString()); + Assert.assertEquals(NODE_HELP, normalize(sysOutStream.toString())); } @Test @@ -564,7 +566,7 @@ public void testKillApplication() throws Exception { newApplicationReport); result = cli.run(new String[] { "-kill", applicationId.toString() }); assertEquals(0, result); - verify(client).killApplication(any(ApplicationId.class)); + verify(client).killApplication(any(ApplicationId.class), anyString()); verify(sysOut).println("Killing application application_1234_0005"); doThrow(new ApplicationNotFoundException("Application with id '" @@ -875,8 +877,8 @@ public void testMissingArguments() throws Exception { ApplicationCLI cli = createAndGetAppCLI(); int result = cli.run(new String[] { "-status" }); Assert.assertEquals(result, -1); - Assert.assertEquals(String.format("Missing argument for options%n%1s", - createApplicationCLIHelpMessage()), sysOutStream.toString()); + Assert.assertEquals("Missing argument for options " + APP_HELP, + normalize(sysOutStream.toString())); sysOutStream.reset(); NodeCLI nodeCLI = new NodeCLI(); @@ -885,8 +887,8 @@ public void testMissingArguments() throws Exception { nodeCLI.setSysErrPrintStream(sysErr); result = nodeCLI.run(new String[] { "-status" }); Assert.assertEquals(result, -1); - Assert.assertEquals(String.format("Missing argument for options%n%1s", - createNodeCLIHelpMessage()), sysOutStream.toString()); + Assert.assertEquals("Missing argument for options " + NODE_HELP, + normalize(sysOutStream.toString())); } private void verifyUsageInfo(YarnCLI cli) throws Exception { @@ -915,7 +917,7 @@ private ApplicationCLI createAndGetAppCLI() { return cli; } - private String createApplicationCLIHelpMessage() throws IOException { + private static String createApplicationCLIHelpMessage() { ByteArrayOutputStream baos = new ByteArrayOutputStream(); PrintWriter pw = new PrintWriter(baos); pw.println("usage: application"); @@ -923,13 +925,13 @@ private String createApplicationCLIHelpMessage() throws IOException { pw.println(" on input comma-separated list of application"); pw.println(" states. The valid application state can be one"); pw.println(" of the following:"); - pw.println(" ALL,NEW,NEW_SAVING,SUBMITTED,ACCEPTED,RUNNING,"); - pw.println(" FINISHED,FAILED,KILLED"); + pw.println(" ALL, NEW, NEW_SAVING, SUBMITTED, ACCEPTED, RUNNING,"); + pw.println(" FINISHED, FAILED, KILLED"); pw.println(" -appTypes Works with -list to filter applications based"); pw.println(" on input comma-separated list of application"); pw.println(" types."); pw.println(" -help Displays help for all commands."); - pw.println(" -kill Kills the application."); + pw.println(" -kill Kills the application."); pw.println(" -list List applications from the RM. Supports"); pw.println(" optional use of -appTypes to filter"); pw.println(" applications based on application type, and"); @@ -937,11 +939,15 @@ private String createApplicationCLIHelpMessage() throws IOException { pw.println(" application state"); pw.println(" -status Prints the status of the application."); pw.close(); - String appsHelpStr = baos.toString("UTF-8"); - return appsHelpStr; + + try { + return normalize(baos.toString("UTF-8")); + } catch (UnsupportedEncodingException infeasible) { + return infeasible.toString(); + } } - private String createNodeCLIHelpMessage() throws IOException { + private static String createNodeCLIHelpMessage() { ByteArrayOutputStream baos = new ByteArrayOutputStream(); PrintWriter pw = new PrintWriter(baos); pw.println("usage: node"); @@ -953,7 +959,15 @@ private String createNodeCLIHelpMessage() throws IOException { pw.println(" comma-separated list of node states."); pw.println(" -status Prints the status report of the node."); pw.close(); - String nodesHelpStr = baos.toString("UTF-8"); - return nodesHelpStr; + + try { + return normalize(baos.toString("UTF-8")); + } catch (UnsupportedEncodingException infeasible) { + return infeasible.toString(); + } + } + + private static String normalize(String s) { + return SPACES_PATTERN.matcher(s).replaceAll(" "); // single space } } diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/impl/pb/KillApplicationRequestPBImpl.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/impl/pb/KillApplicationRequestPBImpl.java index db97367..99ca603 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/impl/pb/KillApplicationRequestPBImpl.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/impl/pb/KillApplicationRequestPBImpl.java @@ -38,8 +38,8 @@ boolean viaProto = false; private ApplicationId applicationId = null; - - + private String reason; + public KillApplicationRequestPBImpl() { builder = KillApplicationRequestProto.newBuilder(); } @@ -80,6 +80,9 @@ private void mergeLocalToBuilder() { if (this.applicationId != null) { builder.setApplicationId(convertToProtoFormat(this.applicationId)); } + if (reason != null) { + builder.setReason(reason); + } } private void mergeLocalToProto() { @@ -119,6 +122,27 @@ public void setApplicationId(ApplicationId applicationId) { this.applicationId = applicationId; } + @Override + public String getReason() { + if (reason != null) { + return reason; + } + final KillApplicationRequestProtoOrBuilder p = viaProto ? proto : builder; + if (p.hasReason()) { + reason = p.getReason(); + } + return reason; + } + + @Override + public void setReason(String reason) { + maybeInitBuilder(); + if (reason == null) { + builder.clearReason(); + } + this.reason = reason; + } + private ApplicationIdPBImpl convertFromProtoFormat(ApplicationIdProto p) { return new ApplicationIdPBImpl(p); } diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ClientRMService.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ClientRMService.java index cd2226f..959aad6 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ClientRMService.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ClientRMService.java @@ -90,8 +90,7 @@ import org.apache.hadoop.yarn.security.client.RMDelegationTokenIdentifier; import org.apache.hadoop.yarn.server.resourcemanager.RMAuditLogger.AuditConstants; import org.apache.hadoop.yarn.server.resourcemanager.rmapp.RMApp; -import org.apache.hadoop.yarn.server.resourcemanager.rmapp.RMAppEvent; -import org.apache.hadoop.yarn.server.resourcemanager.rmapp.RMAppEventType; +import org.apache.hadoop.yarn.server.resourcemanager.rmapp.RMAppKillEvent; import org.apache.hadoop.yarn.server.resourcemanager.rmnode.RMNode; import org.apache.hadoop.yarn.server.resourcemanager.scheduler.SchedulerNodeReport; import org.apache.hadoop.yarn.server.resourcemanager.scheduler.YarnScheduler; @@ -347,6 +346,7 @@ public KillApplicationResponse forceKillApplication( KillApplicationRequest request) throws YarnException { ApplicationId applicationId = request.getApplicationId(); + final String reason = request.getReason(); UserGroupInformation callerUGI; try { @@ -386,7 +386,7 @@ public KillApplicationResponse forceKillApplication( return KillApplicationResponse.newInstance(true); } else { this.rmContext.getDispatcher().getEventHandler() - .handle(new RMAppEvent(applicationId, RMAppEventType.KILL)); + .handle(new RMAppKillEvent(applicationId, reason)); return KillApplicationResponse.newInstance(false); } } diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/rmapp/RMAppImpl.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/rmapp/RMAppImpl.java index 0bf7c81..afc0d0e 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/rmapp/RMAppImpl.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/rmapp/RMAppImpl.java @@ -929,6 +929,11 @@ private static String getAppKilledDiagnostics() { @SuppressWarnings("unchecked") @Override public void transition(RMAppImpl app, RMAppEvent event) { + final RMAppKillEvent killEvent = (RMAppKillEvent)event; + final String reason = killEvent.getReason(); + if (reason != null) { + app.diagnostics.append("user diagnostics: \"" + reason + "\";"); + } app.stateBeforeKilling = app.getState(); app.handler.handle(new RMAppAttemptEvent(app.currentAttempt .getAppAttemptId(), RMAppAttemptEventType.KILL)); diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/rmapp/RMAppKillEvent.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/rmapp/RMAppKillEvent.java new file mode 100644 index 0000000..98abea6 --- /dev/null +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/rmapp/RMAppKillEvent.java @@ -0,0 +1,37 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.yarn.server.resourcemanager.rmapp; + +import org.apache.hadoop.yarn.api.records.ApplicationId; + +public class RMAppKillEvent extends RMAppEvent { + private final String reason; + + public RMAppKillEvent(ApplicationId applicationId, String reason) { + super(applicationId, RMAppEventType.KILL); + this.reason = reason; + } + + public RMAppKillEvent(ApplicationId applicationId) { + this(applicationId, null); + } + + public String getReason() { + return reason; + } +} diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/rmapp/TestRMAppTransitions.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/rmapp/TestRMAppTransitions.java index ba255d3..73eefbd 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/rmapp/TestRMAppTransitions.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/rmapp/TestRMAppTransitions.java @@ -491,8 +491,7 @@ public void testAppNewKill() throws IOException { RMApp application = createNewTestApp(null); // NEW => KILLED event RMAppEventType.KILL - RMAppEvent event = - new RMAppEvent(application.getApplicationId(), RMAppEventType.KILL); + RMAppEvent event = new RMAppKillEvent(application.getApplicationId()); application.handle(event); rmDispatcher.await(); sendAppUpdateSavedEvent(application); @@ -520,8 +519,7 @@ public void testAppNewSavingKill() throws IOException { RMApp application = testCreateAppNewSaving(null); // NEW_SAVING => KILLED event RMAppEventType.KILL - RMAppEvent event = - new RMAppEvent(application.getApplicationId(), RMAppEventType.KILL); + RMAppEvent event = new RMAppKillEvent(application.getApplicationId()); application.handle(event); rmDispatcher.await(); sendAppUpdateSavedEvent(application); @@ -563,8 +561,7 @@ public void testAppSubmittedKill() throws IOException, InterruptedException { LOG.info("--- START: testAppSubmittedKill---"); RMApp application = testCreateAppSubmittedNoRecovery(null); // SUBMITTED => KILLED event RMAppEventType.KILL - RMAppEvent event = new RMAppEvent(application.getApplicationId(), - RMAppEventType.KILL); + RMAppEvent event = new RMAppKillEvent(application.getApplicationId()); application.handle(event); rmDispatcher.await(); assertAppAndAttemptKilled(application); @@ -608,8 +605,7 @@ public void testAppAcceptedKill() throws IOException, InterruptedException { LOG.info("--- START: testAppAcceptedKill ---"); RMApp application = testCreateAppAccepted(null); // ACCEPTED => KILLED event RMAppEventType.KILL - RMAppEvent event = new RMAppEvent(application.getApplicationId(), - RMAppEventType.KILL); + RMAppEvent event = new RMAppKillEvent(application.getApplicationId()); application.handle(event); rmDispatcher.await(); assertAppAndAttemptKilled(application); @@ -621,8 +617,7 @@ public void testAppRunningKill() throws IOException { RMApp application = testCreateAppRunning(null); // RUNNING => KILLED event RMAppEventType.KILL - RMAppEvent event = - new RMAppEvent(application.getApplicationId(), RMAppEventType.KILL); + RMAppEvent event = new RMAppKillEvent(application.getApplicationId()); application.handle(event); rmDispatcher.await(); @@ -683,7 +678,7 @@ public void testAppRunningFailed() throws IOException { assertFailed(application, ".*Failing the application.*"); // FAILED => FAILED event RMAppEventType.KILL - event = new RMAppEvent(application.getApplicationId(), RMAppEventType.KILL); + event = new RMAppKillEvent(application.getApplicationId()); application.handle(event); rmDispatcher.await(); assertFailed(application, ".*Failing the application.*"); @@ -695,8 +690,7 @@ public void testAppAtFinishingIgnoreKill() throws IOException { RMApp application = testCreateAppFinishing(null); // FINISHING => FINISHED event RMAppEventType.KILL - RMAppEvent event = - new RMAppEvent(application.getApplicationId(), RMAppEventType.KILL); + RMAppEvent event = new RMAppKillEvent(application.getApplicationId()); application.handle(event); rmDispatcher.await(); assertAppState(RMAppState.FINISHING, application); @@ -735,8 +729,7 @@ public void testAppFinishedFinished() throws IOException { RMApp application = testCreateAppFinished(null, ""); // FINISHED => FINISHED event RMAppEventType.KILL - RMAppEvent event = - new RMAppEvent(application.getApplicationId(), RMAppEventType.KILL); + RMAppEvent event = new RMAppKillEvent(application.getApplicationId()); application.handle(event); rmDispatcher.await(); assertTimesAtFinish(application); @@ -762,8 +755,7 @@ public void testAppFailedFailed() throws IOException { assertAppState(RMAppState.FAILED, application); // FAILED => FAILED event RMAppEventType.KILL - event = - new RMAppEvent(application.getApplicationId(), RMAppEventType.KILL); + event = new RMAppKillEvent(application.getApplicationId()); application.handle(event); rmDispatcher.await(); assertTimesAtFinish(application); @@ -780,8 +772,7 @@ public void testAppKilledKilled() throws IOException { RMApp application = testCreateAppRunning(null); // RUNNING => KILLED event RMAppEventType.KILL - RMAppEvent event = - new RMAppEvent(application.getApplicationId(), RMAppEventType.KILL); + RMAppEvent event = new RMAppKillEvent(application.getApplicationId()); application.handle(event); rmDispatcher.await(); sendAttemptUpdateSavedEvent(application); @@ -808,7 +799,7 @@ public void testAppKilledKilled() throws IOException { // KILLED => KILLED event RMAppEventType.KILL - event = new RMAppEvent(application.getApplicationId(), RMAppEventType.KILL); + event = new RMAppKillEvent(application.getApplicationId()); application.handle(event); rmDispatcher.await(); assertTimesAtFinish(application);