diff --git a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/main/java/org/apache/hadoop/mapred/ResourceMgrDelegate.java b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/main/java/org/apache/hadoop/mapred/ResourceMgrDelegate.java index 2f3b96caa1a..a5d0e1d7262 100644 --- a/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/main/java/org/apache/hadoop/mapred/ResourceMgrDelegate.java +++ b/hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-jobclient/src/main/java/org/apache/hadoop/mapred/ResourceMgrDelegate.java @@ -501,4 +501,10 @@ public class ResourceMgrDelegate extends YarnClient { throws YarnException, IOException { client.signalToContainer(containerId, command); } + + @Override + public void killApplication(ApplicationId appId, String diagnostics) + throws YarnException, IOException { + client.killApplication(appId, diagnostics); + } } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/KillApplicationRequest.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/KillApplicationRequest.java index 606cf4eff4c..a7679a0fb62 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/KillApplicationRequest.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/KillApplicationRequest.java @@ -20,6 +20,7 @@ package org.apache.hadoop.yarn.api.protocolrecords; import org.apache.hadoop.classification.InterfaceAudience.Public; import org.apache.hadoop.classification.InterfaceStability.Stable; +import org.apache.hadoop.classification.InterfaceStability.Unstable; import org.apache.hadoop.yarn.api.ApplicationClientProtocol; import org.apache.hadoop.yarn.api.records.ApplicationId; import org.apache.hadoop.yarn.util.Records; @@ -57,4 +58,21 @@ public abstract class KillApplicationRequest { @Public @Stable public abstract void setApplicationId(ApplicationId applicationId); + + /** + * Get the diagnostics to which the application is being killed. + * @return diagnostics to which the application is being killed + */ + @Public + @Unstable + public abstract String getDiagnostics(); + + /** + * Set the diagnostics to which the application is being killed. + * @param diagnostics diagnostics to which the application is being + * killed + */ + @Public + @Unstable + public abstract void setDiagnostics(String diagnostics); } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/proto/yarn_service_protos.proto b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/proto/yarn_service_protos.proto index de05e1bd162..c81ba4246f6 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/proto/yarn_service_protos.proto +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-api/src/main/proto/yarn_service_protos.proto @@ -153,6 +153,7 @@ message FailApplicationAttemptResponseProto { message KillApplicationRequestProto { optional ApplicationIdProto application_id = 1; + optional string diagnostics = 2; } message KillApplicationResponseProto { diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/YarnClient.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/YarnClient.java index e1b9817900e..bdaa2c8f54b 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/YarnClient.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/YarnClient.java @@ -172,6 +172,20 @@ public abstract class YarnClient extends AbstractService { public abstract void killApplication(ApplicationId applicationId) throws YarnException, IOException; + /** + *
+ * Kill an application identified by given ID. + *
+ * @param applicationId {@link ApplicationId} of the application that needs to + * be killed + * @param diagnostics for killing an application. + * @throws YarnException in case of errors or if YARN rejects the request due + * to access-control restrictions. + * @throws IOException + */ + public abstract void killApplication(ApplicationId applicationId, + String diagnostics) throws YarnException, IOException; + /** ** Get a report of the given Application. diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/impl/YarnClientImpl.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/impl/YarnClientImpl.java index f6d12d8a31c..669231da376 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/impl/YarnClientImpl.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-client/src/main/java/org/apache/hadoop/yarn/client/api/impl/YarnClientImpl.java @@ -400,10 +400,21 @@ public class YarnClientImpl extends YarnClient { @Override public void killApplication(ApplicationId applicationId) throws YarnException, IOException { + killApplication(applicationId, null); + } + + @Override + public void killApplication(ApplicationId applicationId, String diagnostics) + throws YarnException, IOException { + KillApplicationRequest request = Records.newRecord(KillApplicationRequest.class); request.setApplicationId(applicationId); + if (diagnostics != null) { + request.setDiagnostics(diagnostics); + } + try { int pollCount = 0; long startTime = System.currentTimeMillis(); @@ -417,14 +428,15 @@ public class YarnClientImpl extends YarnClient { } long elapsedMillis = System.currentTimeMillis() - startTime; - if (enforceAsyncAPITimeout() && - elapsedMillis >= this.asyncApiPollTimeoutMillis) { - throw new YarnException("Timed out while waiting for application " + - applicationId + " to be killed."); + if (enforceAsyncAPITimeout() + && elapsedMillis >= this.asyncApiPollTimeoutMillis) { + throw new YarnException("Timed out while waiting for application " + + applicationId + " to be killed."); } if (++pollCount % 10 == 0) { - LOG.info("Waiting for application " + applicationId + " to be killed."); + LOG.info( + "Waiting for application " + applicationId + " to be killed."); } Thread.sleep(asyncApiPollIntervalMillis); } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/impl/pb/KillApplicationRequestPBImpl.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/impl/pb/KillApplicationRequestPBImpl.java index db973676828..0520222f938 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/impl/pb/KillApplicationRequestPBImpl.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/api/protocolrecords/impl/pb/KillApplicationRequestPBImpl.java @@ -127,6 +127,24 @@ public class KillApplicationRequestPBImpl extends KillApplicationRequest { return ((ApplicationIdPBImpl)t).getProto(); } + @Override + public String getDiagnostics() { + KillApplicationRequestProtoOrBuilder p = viaProto ? proto : builder; + if (!p.hasDiagnostics()) { + return null; + } + return (p.getDiagnostics()); + } + + @Override + public void setDiagnostics(String diagnostics) { + maybeInitBuilder(); + if (diagnostics == null) { + builder.clearDiagnostics(); + return; + } + builder.setDiagnostics(diagnostics); + } } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ClientRMService.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ClientRMService.java index b1276cc57f8..b92c062ec0e 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ClientRMService.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/ClientRMService.java @@ -750,15 +750,25 @@ public class ClientRMService extends AbstractService implements return KillApplicationResponse.newInstance(true); } - String message = "Kill application " + applicationId + " received from " - + callerUGI; + StringBuilder message = new StringBuilder(); + message.append("Application ").append(applicationId) + .append(" was killed by user ").append(callerUGI.getShortUserName()); + InetAddress remoteAddress = Server.getRemoteIp(); if (null != remoteAddress) { - message += " at " + remoteAddress.getHostAddress(); + message.append(" at ").append(remoteAddress.getHostAddress()); } + + String diagnostics = org.apache.commons.lang.StringUtils + .trimToNull(request.getDiagnostics()); + if (diagnostics != null) { + message.append(" with diagnostic message: "); + message.append(diagnostics); + } + this.rmContext.getDispatcher().getEventHandler() - .handle(new RMAppKillByClientEvent(applicationId, message, callerUGI, - remoteAddress)); + .handle(new RMAppKillByClientEvent(applicationId, message.toString(), + callerUGI, remoteAddress)); // For UnmanagedAMs, return true so they don't retry return KillApplicationResponse.newInstance( diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/RMWebServices.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/RMWebServices.java index cc0a0816cae..c7ddaabae2d 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/RMWebServices.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/RMWebServices.java @@ -789,7 +789,7 @@ public class RMWebServices { // allow users to kill the app if (targetState.getState().equals(YarnApplicationState.KILLED.toString())) { - return killApp(app, callerUGI, hsr); + return killApp(app, callerUGI, hsr, targetState.getDiagnostics()); } throw new BadRequestException("Only '" + YarnApplicationState.KILLED.toString() @@ -1006,7 +1006,8 @@ public class RMWebServices { } protected Response killApp(RMApp app, UserGroupInformation callerUGI, - HttpServletRequest hsr) throws IOException, InterruptedException { + HttpServletRequest hsr, final String diagnostic) + throws IOException, InterruptedException { if (app == null) { throw new IllegalArgumentException("app cannot be null"); @@ -1023,6 +1024,9 @@ public class RMWebServices { YarnException { KillApplicationRequest req = KillApplicationRequest.newInstance(appid); + if (diagnostic != null) { + req.setDiagnostics(diagnostic); + } return rm.getClientRMService().forceKillApplication(req); } }); diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/AppState.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/AppState.java index e8f1cc301a3..533200e2d48 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/AppState.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/AppState.java @@ -27,6 +27,7 @@ import javax.xml.bind.annotation.XmlRootElement; public class AppState { String state; + private String diagnostics; public AppState() { } @@ -43,4 +44,11 @@ public class AppState { return this.state; } + public String getDiagnostics() { + return diagnostics; + } + + public void setDiagnostics(String diagnostics) { + this.diagnostics = diagnostics; + } } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestClientRMService.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestClientRMService.java index 2d594b6dc99..834120fb79a 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestClientRMService.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/TestClientRMService.java @@ -501,7 +501,8 @@ public class TestClientRMService { @Test public void testForceKillApplication() throws Exception { YarnConfiguration conf = new YarnConfiguration(); - MockRM rm = new MockRM(); + conf.setBoolean(MockRM.ENABLE_WEBAPP, true); + MockRM rm = new MockRM(conf); rm.init(conf); rm.start(); @@ -517,6 +518,8 @@ public class TestClientRMService { KillApplicationRequest killRequest1 = KillApplicationRequest.newInstance(app1.getApplicationId()); + String diagnostic = "message1"; + killRequest1.setDiagnostics(diagnostic); KillApplicationRequest killRequest2 = KillApplicationRequest.newInstance(app2.getApplicationId()); @@ -534,6 +537,8 @@ public class TestClientRMService { killAttemptCount > 1); assertEquals("Incorrect number of apps in the RM", 1, rmService.getApplications(getRequest).getApplicationList().size()); + assertTrue("Diagnostic message is incorrect", + app1.getDiagnostics().toString().contains(diagnostic)); KillApplicationResponse killResponse2 = rmService.forceKillApplication(killRequest2); diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesAppsModification.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesAppsModification.java index 455f8bc9c15..2b6b1287883 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesAppsModification.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesAppsModification.java @@ -362,6 +362,7 @@ public class TestRMWebServicesAppsModification extends JerseyTestBase { { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }; MediaType[] contentTypes = { MediaType.APPLICATION_JSON_TYPE, MediaType.APPLICATION_XML_TYPE }; + String diagnostic = "message1"; for (String mediaType : mediaTypes) { for (MediaType contentType : contentTypes) { RMApp app = rm.submitApp(CONTAINER_MB, "", webserviceUserName); @@ -369,6 +370,7 @@ public class TestRMWebServicesAppsModification extends JerseyTestBase { AppState targetState = new AppState(YarnApplicationState.KILLED.toString()); + targetState.setDiagnostics(diagnostic); Object entity; if (contentType.equals(MediaType.APPLICATION_JSON_TYPE)) { @@ -423,6 +425,8 @@ public class TestRMWebServicesAppsModification extends JerseyTestBase { } else { verifyAppStateXML(response, RMAppState.KILLED); } + assertTrue("Diagnostic message is incorrect", + app.getDiagnostics().toString().contains(diagnostic)); break; } }