From d0372dc613136910160e9d42bd5eaa0d4bde2356 Mon Sep 17 00:00:00 2001 From: Naganarasimha Date: Fri, 23 Sep 2016 06:30:49 +0530 Subject: [PATCH] YARN-3692. Allow REST API to set a user generated message when killing an application. Contributed by Rohith Sharma K S --- .../hadoop/mapred/ResourceMgrDelegate.java | 6 +++++ .../KillApplicationRequest.java | 18 +++++++++++++++ .../src/main/proto/yarn_service_protos.proto | 1 + .../hadoop/yarn/client/api/YarnClient.java | 14 ++++++++++++ .../yarn/client/api/impl/YarnClientImpl.java | 22 ++++++++++++++----- .../impl/pb/KillApplicationRequestPBImpl.java | 18 +++++++++++++++ .../resourcemanager/ClientRMService.java | 20 ++++++++++++----- .../resourcemanager/webapp/RMWebServices.java | 8 +++++-- .../resourcemanager/webapp/dao/AppState.java | 8 +++++++ .../resourcemanager/TestClientRMService.java | 7 +++++- .../TestRMWebServicesAppsModification.java | 4 ++++ 11 files changed, 113 insertions(+), 13 deletions(-) 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 159b51889f..c302553f36 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 @@ -511,4 +511,10 @@ public void signalToContainer(ContainerId containerId, 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 606cf4eff4..a7679a0fb6 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 @@ 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 static KillApplicationRequest newInstance(ApplicationId applicationId) { @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 97eaa5c4e2..1385ea4849 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 @@ -156,6 +156,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 619ea0d252..4cac2c24da 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 void failApplicationAttempt( 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 b91fde0ba2..7760521f88 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 @@ -405,10 +405,21 @@ public void failApplicationAttempt(ApplicationAttemptId attemptId) @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(); @@ -422,14 +433,15 @@ public void killApplication(ApplicationId applicationId) } 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 db97367682..0520222f93 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 @@ private ApplicationIdProto convertToProtoFormat(ApplicationId t) { 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 70e1863fdb..e9bd230696 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 @@ -775,15 +775,25 @@ public KillApplicationResponse forceKillApplication( 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 4305fd52ae..ef6fd068a2 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 @@ -925,7 +925,7 @@ public Response updateAppState(AppState targetState, // 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() @@ -1141,7 +1141,8 @@ public NodeLabelsInfo getLabelsOnNode(@Context HttpServletRequest hsr, } protected Response killApp(RMApp app, UserGroupInformation callerUGI, - HttpServletRequest hsr) throws IOException, InterruptedException { + HttpServletRequest hsr, String diagnostic) + throws IOException, InterruptedException { if (app == null) { throw new IllegalArgumentException("app cannot be null"); @@ -1158,6 +1159,9 @@ public KillApplicationResponse run() throws IOException, 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 e8f1cc301a..533200e2d4 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 @@ public class AppState { String state; + private String diagnostics; public AppState() { } @@ -43,4 +44,11 @@ public String getState() { 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 2a7971e814..cee9086514 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 @@ -506,7 +506,8 @@ public void testForceKillNonExistingApplication() throws YarnException { @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(); @@ -522,6 +523,8 @@ public void testForceKillApplication() throws Exception { KillApplicationRequest killRequest1 = KillApplicationRequest.newInstance(app1.getApplicationId()); + String diagnostic = "message1"; + killRequest1.setDiagnostics(diagnostic); KillApplicationRequest killRequest2 = KillApplicationRequest.newInstance(app2.getApplicationId()); @@ -539,6 +542,8 @@ public void testForceKillApplication() throws Exception { 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 53ef031da8..37e44d49f0 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 @@ -359,6 +359,7 @@ public void testSingleAppKill() throws Exception { { 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); @@ -366,6 +367,7 @@ public void testSingleAppKill() throws Exception { AppState targetState = new AppState(YarnApplicationState.KILLED.toString()); + targetState.setDiagnostics(diagnostic); Object entity; if (contentType.equals(MediaType.APPLICATION_JSON_TYPE)) { @@ -425,6 +427,8 @@ public void testSingleAppKill() throws Exception { } else { verifyAppStateXML(response, RMAppState.KILLED); } + assertTrue("Diagnostic message is incorrect", + app.getDiagnostics().toString().contains(diagnostic)); break; } }