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 60fbfcd..5aac857 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 @@ -19,6 +19,7 @@ package org.apache.hadoop.yarn.server.resourcemanager.webapp; import java.io.IOException; +import java.security.PrivilegedExceptionAction; import java.util.Arrays; import java.util.Collection; import java.util.EnumSet; @@ -32,18 +33,24 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.ws.rs.GET; +import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.Status; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.security.UserGroupInformation; +import org.apache.hadoop.security.authorize.AuthorizationException; import org.apache.hadoop.yarn.api.protocolrecords.GetApplicationsRequest; +import org.apache.hadoop.yarn.api.protocolrecords.KillApplicationRequest; +import org.apache.hadoop.yarn.api.protocolrecords.KillApplicationResponse; import org.apache.hadoop.yarn.api.records.ApplicationAccessType; import org.apache.hadoop.yarn.api.records.ApplicationId; import org.apache.hadoop.yarn.api.records.ApplicationReport; @@ -56,6 +63,8 @@ import org.apache.hadoop.yarn.exceptions.YarnRuntimeException; import org.apache.hadoop.yarn.factories.RecordFactory; import org.apache.hadoop.yarn.factory.providers.RecordFactoryProvider; +import org.apache.hadoop.yarn.server.resourcemanager.RMAuditLogger; +import org.apache.hadoop.yarn.server.resourcemanager.RMAuditLogger.AuditConstants; import org.apache.hadoop.yarn.server.resourcemanager.RMServerUtils; import org.apache.hadoop.yarn.server.resourcemanager.ResourceManager; import org.apache.hadoop.yarn.server.resourcemanager.rmapp.RMApp; @@ -590,4 +599,121 @@ public AppAttemptsInfo getAppAttempts(@PathParam("appid") String appId) { return appAttemptsInfo; } + + // can't return POJO because we can't control the status code + // it's always set to 200 when we need to allow it to be set + // to 202 + + @PUT + @Path("/apps/{appid}/kill") + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + public Response killApp(@Context HttpServletRequest hsr, + @PathParam("appid") String appId) throws AuthorizationException, + YarnException, InterruptedException, IOException { + + init(); + UserGroupInformation callerUGI = getCallerUserGroupInformation(hsr); + if(callerUGI == null) { + throw new AuthorizationException("Unable to obtain user name, user not authenticated"); + } + String userName = callerUGI.getUserName(); + RMApp app = null; + try { + app = appIdToRMApp(appId); + } + catch(NotFoundException e) { + RMAuditLogger.logFailure(userName, + AuditConstants.KILL_APP_REQUEST, "UNKNOWN", "RMWebService", + "Trying to kill an absent application " + appId); + throw e; + } + + if(app != null && !hasAppAcess(app, callerUGI, ApplicationAccessType.MODIFY_APP)) { + String msg = "Unauthorized attempt to kill appid " + + appId + "by remote user " + userName; + RMAuditLogger.logFailure(userName, AuditConstants.KILL_APP_REQUEST, + "UNKNOWN", "RMWebService", "Unauthorized attempt to kill appid " + appId); + return Response.status(Status.FORBIDDEN).entity(msg).build(); + } + + final ApplicationId appid = app.getApplicationId(); + KillApplicationResponse resp = callerUGI.doAs(new PrivilegedExceptionAction() { + @Override + public KillApplicationResponse run() throws IOException, YarnException { + KillApplicationRequest req = recordFactory.newRecordInstance(KillApplicationRequest.class); + req.setApplicationId(appid); + return rm.getClientRMService().forceKillApplication(req); + } + }); + + AppInfo appInfo = new AppInfo(app, hasAccess(app, hsr), hsr.getScheme() + "://"); + + if (resp.getIsKillCompleted()) { + RMAuditLogger.logSuccess(userName, AuditConstants.KILL_APP_REQUEST, + "RMWebService", app.getApplicationId()); + } else { + StringBuffer url = hsr.getRequestURL(); + String appUrl = url.substring(0, url.length() - "/kill".length()); + return Response.status(Status.ACCEPTED) + .entity(appInfo) + .header("Location", appUrl).build(); + } + + return Response.status(Status.OK).entity(new AppInfo(app, hasAccess(app, hsr), + hsr.getScheme() + "://")).build(); + } + + private RMApp appIdToRMApp(String appId) { + + if(appId == null || appId.isEmpty()) { + throw new NotFoundException("appId, " + appId + ", is empty or null"); + } + ApplicationId id; + try { + id = ConverterUtils.toApplicationId(recordFactory, appId); + } + catch(NumberFormatException e) { + throw new NotFoundException("appId is invalid"); + } + if(id == null) { + throw new NotFoundException("appId is invalid"); + } + RMApp app = rm.getRMContext().getRMApps().get(id); + if(app == null) { + throw new NotFoundException("app with id: " + appId + " not found"); + } + return app; + } + + private UserGroupInformation getCallerUserGroupInformation(HttpServletRequest hsr) { + + String remoteUser = hsr.getRemoteUser(); + UserGroupInformation callerUGI = null; + if (remoteUser != null) { + callerUGI = UserGroupInformation.createRemoteUser(remoteUser); + } + + return callerUGI; + } + + protected Boolean hasAppAcess(RMApp app, UserGroupInformation callerUGI, ApplicationAccessType type) { + if (callerUGI != null) { + if(!this.aclsManager.checkAccess(callerUGI, type, + app.getUser(), app.getApplicationId())) { + return false; + } + } + return true; + } + + protected Boolean hasQueueAcess(RMApp app, UserGroupInformation callerUGI, QueueACL type) { + if (callerUGI != null) { + if(!this.queueACLsManager.checkAccess(callerUGI, type, + app.getQueue())) { + return false; + } + } + return true; + } + } 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/TestRMCustomAuthFilter.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMCustomAuthFilter.java new file mode 100644 index 0000000..d0a0404 --- /dev/null +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMCustomAuthFilter.java @@ -0,0 +1,57 @@ +/** + * 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.webapp; + +import java.util.Enumeration; +import java.util.Properties; + +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; + +import org.apache.hadoop.security.authentication.server.AuthenticationFilter; +import org.apache.hadoop.security.authentication.server.PseudoAuthenticationHandler; + +import com.google.inject.Singleton; + +/* + * Helper class to allow testing of RM web services which require authorization + * Add this class as a filter in the Guice injector for the MockRM + * + */ + +@Singleton +public class TestRMCustomAuthFilter extends AuthenticationFilter { + + @Override + protected Properties getConfiguration(String configPrefix, FilterConfig filterConfig) throws ServletException { + Properties props = new Properties(); + Enumeration names = filterConfig.getInitParameterNames(); + while (names.hasMoreElements()) { + String name = (String) names.nextElement(); + if (name.startsWith(configPrefix)) { + String value = filterConfig.getInitParameter(name); + props.put(name.substring(configPrefix.length()), value); + } + } + props.put(AuthenticationFilter.AUTH_TYPE, "simple"); + props.put(PseudoAuthenticationHandler.ANONYMOUS_ALLOWED, "false"); + return props; + } + +} 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/TestRMWebServicesApps.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesApps.java index 45b3803..71a79d4 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesApps.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesApps.java @@ -22,12 +22,15 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import java.io.IOException; import java.io.StringReader; +import java.util.Arrays; import java.util.Collection; import javax.ws.rs.core.MediaType; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.security.UserGroupInformation; @@ -53,17 +56,23 @@ import org.codehaus.jettison.json.JSONArray; import org.codehaus.jettison.json.JSONException; import org.codehaus.jettison.json.JSONObject; +import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; +import org.xml.sax.SAXException; import com.google.inject.Guice; import com.google.inject.Injector; import com.google.inject.servlet.GuiceServletContextListener; import com.google.inject.servlet.ServletModule; +import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.ClientResponse; import com.sun.jersey.api.client.ClientResponse.Status; import com.sun.jersey.api.client.UniformInterfaceException; @@ -73,40 +82,83 @@ import com.sun.jersey.test.framework.JerseyTest; import com.sun.jersey.test.framework.WebAppDescriptor; +@RunWith(Parameterized.class) public class TestRMWebServicesApps extends JerseyTest { private static MockRM rm; private static final int CONTAINER_MB = 1024; - - private Injector injector = Guice.createInjector(new ServletModule() { - @Override - protected void configureServlets() { - bind(JAXBContextResolver.class); - bind(RMWebServices.class); - bind(GenericExceptionHandler.class); - Configuration conf = new Configuration(); - conf.setInt(YarnConfiguration.RM_AM_MAX_ATTEMPTS, - YarnConfiguration.DEFAULT_RM_AM_MAX_ATTEMPTS); - conf.setClass(YarnConfiguration.RM_SCHEDULER, FifoScheduler.class, - ResourceScheduler.class); - rm = new MockRM(conf); - bind(ResourceManager.class).toInstance(rm); - bind(RMContext.class).toInstance(rm.getRMContext()); - bind(ApplicationACLsManager.class).toInstance( - rm.getApplicationACLsManager()); - bind(QueueACLsManager.class).toInstance(rm.getQueueACLsManager()); - serve("/*").with(GuiceContainer.class); - } - }); - + + private Injector injector; + private String webserviceUserName = "testuser"; + public class GuiceServletConfig extends GuiceServletContextListener { - + @Override protected Injector getInjector() { return injector; } } + + private Injector getNoAuthInjector() { + return Guice.createInjector(new ServletModule() { + @Override + protected void configureServlets() { + bind(JAXBContextResolver.class); + bind(RMWebServices.class); + bind(GenericExceptionHandler.class); + Configuration conf = new Configuration(); + conf.setInt(YarnConfiguration.RM_AM_MAX_ATTEMPTS, + YarnConfiguration.DEFAULT_RM_AM_MAX_ATTEMPTS); + conf.setClass(YarnConfiguration.RM_SCHEDULER, FifoScheduler.class, + ResourceScheduler.class); + rm = new MockRM(conf); + bind(ResourceManager.class).toInstance(rm); + bind(RMContext.class).toInstance(rm.getRMContext()); + bind(ApplicationACLsManager.class).toInstance( + rm.getApplicationACLsManager()); + bind(QueueACLsManager.class).toInstance(rm.getQueueACLsManager()); + serve("/*").with(GuiceContainer.class); + } + }); + } + + private Injector getSimpleAuthInjector() { + return Guice.createInjector(new ServletModule() { + @Override + protected void configureServlets() { + bind(JAXBContextResolver.class); + bind(RMWebServices.class); + bind(GenericExceptionHandler.class); + Configuration conf = new Configuration(); + conf.setInt(YarnConfiguration.RM_AM_MAX_ATTEMPTS, + YarnConfiguration.DEFAULT_RM_AM_MAX_ATTEMPTS); + conf.setClass(YarnConfiguration.RM_SCHEDULER, FifoScheduler.class, + ResourceScheduler.class); + conf.setBoolean(YarnConfiguration.YARN_ACL_ENABLE, true); + // set the admin acls otherwise all users are considered admins + // and we can't test authorization + conf.setStrings(YarnConfiguration.YARN_ADMIN_ACL, "testuser1"); + rm = new MockRM(conf); + bind(ResourceManager.class).toInstance(rm); + bind(RMContext.class).toInstance(rm.getRMContext()); + bind(ApplicationACLsManager.class).toInstance( + rm.getApplicationACLsManager()); + bind(QueueACLsManager.class).toInstance(rm.getQueueACLsManager()); + filter("/*").through(TestRMCustomAuthFilter.class); + serve("/*").with(GuiceContainer.class); + } + }); + } + + @Parameters + public static Collection guiceConfigs() { + return Arrays.asList(new Object[][] { + { 0 }, + { 1 } + }); + } + @Before @Override @@ -114,12 +166,20 @@ public void setUp() throws Exception { super.setUp(); } - public TestRMWebServicesApps() { + public TestRMWebServicesApps(int run) { super(new WebAppDescriptor.Builder( "org.apache.hadoop.yarn.server.resourcemanager.webapp") .contextListenerClass(GuiceServletConfig.class) .filterClass(com.google.inject.servlet.GuiceFilter.class) .contextPath("jersey-guice-filter").servletPath("/").build()); + switch(run) { + case 0: + injector = getNoAuthInjector(); + break; + case 1: + injector = getSimpleAuthInjector(); + break; + } } @Test @@ -158,10 +218,11 @@ public void testAppsXML() throws JSONException, Exception { MockNM amNodeManager = rm.registerNode("127.0.0.1:1234", 2048); RMApp app1 = rm.submitApp(CONTAINER_MB, "testwordcount", "user1"); amNodeManager.nodeHeartbeat(true); - WebResource r = resource(); - ClientResponse response = r.path("ws").path("v1").path("cluster") - .path("apps").accept(MediaType.APPLICATION_XML) + + ClientResponse response = this.constructWebResource("apps") + .accept(MediaType.APPLICATION_XML) .get(ClientResponse.class); + assertEquals(MediaType.APPLICATION_XML_TYPE, response.getType()); String xml = response.getEntity(String.class); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); @@ -185,10 +246,8 @@ public void testAppsXMLMulti() throws JSONException, Exception { rm.submitApp(2048, "testwordcount2", "user1"); amNodeManager.nodeHeartbeat(true); - WebResource r = resource(); - - ClientResponse response = r.path("ws").path("v1").path("cluster") - .path("apps").accept(MediaType.APPLICATION_XML) + ClientResponse response = this.constructWebResource("apps") + .accept(MediaType.APPLICATION_XML) .get(ClientResponse.class); assertEquals(MediaType.APPLICATION_XML_TYPE, response.getType()); String xml = response.getEntity(String.class); @@ -206,10 +265,9 @@ public void testAppsXMLMulti() throws JSONException, Exception { public void testAppsHelper(String path, RMApp app, String media) throws JSONException, Exception { - WebResource r = resource(); - - ClientResponse response = r.path("ws").path("v1").path("cluster") - .path(path).accept(media).get(ClientResponse.class); + + ClientResponse response = this.constructWebResource(path). + accept(media).get(ClientResponse.class); assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); JSONObject json = response.getEntity(JSONObject.class); assertEquals("incorrect number of elements", 1, json.length()); @@ -227,10 +285,8 @@ public void testAppsQueryState() throws JSONException, Exception { MockNM amNodeManager = rm.registerNode("127.0.0.1:1234", 2048); RMApp app1 = rm.submitApp(CONTAINER_MB); amNodeManager.nodeHeartbeat(true); - WebResource r = resource(); - - ClientResponse response = r.path("ws").path("v1").path("cluster") - .path("apps") + + ClientResponse response = this.constructWebResource("apps") .queryParam("state", YarnApplicationState.ACCEPTED.toString()) .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); @@ -254,11 +310,10 @@ public void testAppsQueryStates() throws JSONException, Exception { amNodeManager.nodeHeartbeat(true); - WebResource r = resource(); MultivaluedMapImpl params = new MultivaluedMapImpl(); params.add("states", YarnApplicationState.ACCEPTED.toString()); - ClientResponse response = r.path("ws").path("v1").path("cluster") - .path("apps").queryParams(params) + ClientResponse response = this.constructWebResource("apps") + .queryParams(params) .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); JSONObject json = response.getEntity(JSONObject.class); @@ -270,12 +325,11 @@ public void testAppsQueryStates() throws JSONException, Exception { assertEquals("state not equal to ACCEPTED", "ACCEPTED", array .getJSONObject(0).getString("state")); - r = resource(); params = new MultivaluedMapImpl(); params.add("states", YarnApplicationState.ACCEPTED.toString()); params.add("states", YarnApplicationState.KILLED.toString()); - response = r.path("ws").path("v1").path("cluster") - .path("apps").queryParams(params) + response = this.constructWebResource("apps") + .queryParams(params) .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); json = response.getEntity(JSONObject.class); @@ -303,11 +357,10 @@ public void testAppsQueryStatesComma() throws JSONException, Exception { amNodeManager.nodeHeartbeat(true); - WebResource r = resource(); MultivaluedMapImpl params = new MultivaluedMapImpl(); params.add("states", YarnApplicationState.ACCEPTED.toString()); - ClientResponse response = r.path("ws").path("v1").path("cluster") - .path("apps").queryParams(params) + ClientResponse response = this.constructWebResource("apps") + .queryParams(params) .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); JSONObject json = response.getEntity(JSONObject.class); @@ -319,12 +372,11 @@ public void testAppsQueryStatesComma() throws JSONException, Exception { assertEquals("state not equal to ACCEPTED", "ACCEPTED", array .getJSONObject(0).getString("state")); - r = resource(); params = new MultivaluedMapImpl(); params.add("states", YarnApplicationState.ACCEPTED.toString() + "," + YarnApplicationState.KILLED.toString()); - response = r.path("ws").path("v1").path("cluster") - .path("apps").queryParams(params) + response = this.constructWebResource("apps") + .queryParams(params) .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); json = response.getEntity(JSONObject.class); @@ -348,10 +400,7 @@ public void testAppsQueryStatesNone() throws JSONException, Exception { MockNM amNodeManager = rm.registerNode("127.0.0.1:1234", 2048); rm.submitApp(CONTAINER_MB); amNodeManager.nodeHeartbeat(true); - WebResource r = resource(); - - ClientResponse response = r.path("ws").path("v1").path("cluster") - .path("apps") + ClientResponse response = this.constructWebResource("apps") .queryParam("states", YarnApplicationState.RUNNING.toString()) .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); @@ -367,12 +416,11 @@ public void testAppsQueryStateNone() throws JSONException, Exception { MockNM amNodeManager = rm.registerNode("127.0.0.1:1234", 2048); rm.submitApp(CONTAINER_MB); amNodeManager.nodeHeartbeat(true); - WebResource r = resource(); - ClientResponse response = r.path("ws").path("v1").path("cluster") - .path("apps") + ClientResponse response = this.constructWebResource("apps") .queryParam("state", YarnApplicationState.RUNNING.toString()) .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); + assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); JSONObject json = response.getEntity(JSONObject.class); assertEquals("incorrect number of elements", 1, json.length()); @@ -386,12 +434,11 @@ public void testAppsQueryStatesInvalid() throws JSONException, Exception { MockNM amNodeManager = rm.registerNode("127.0.0.1:1234", 2048); rm.submitApp(CONTAINER_MB); amNodeManager.nodeHeartbeat(true); - WebResource r = resource(); try { - r.path("ws").path("v1").path("cluster").path("apps") - .queryParam("states", "INVALID_test") - .accept(MediaType.APPLICATION_JSON).get(JSONObject.class); + this.constructWebResource("apps") + .queryParam("states", "INVALID_test") + .accept(MediaType.APPLICATION_JSON).get(JSONObject.class); fail("should have thrown exception on invalid state query"); } catch (UniformInterfaceException ue) { ClientResponse response = ue.getResponse(); @@ -423,12 +470,11 @@ public void testAppsQueryStateInvalid() throws JSONException, Exception { MockNM amNodeManager = rm.registerNode("127.0.0.1:1234", 2048); rm.submitApp(CONTAINER_MB); amNodeManager.nodeHeartbeat(true); - WebResource r = resource(); try { - r.path("ws").path("v1").path("cluster").path("apps") - .queryParam("state", "INVALID_test") - .accept(MediaType.APPLICATION_JSON).get(JSONObject.class); + this.constructWebResource("apps") + .queryParam("state", "INVALID_test") + .accept(MediaType.APPLICATION_JSON).get(JSONObject.class); fail("should have thrown exception on invalid state query"); } catch (UniformInterfaceException ue) { ClientResponse response = ue.getResponse(); @@ -460,10 +506,8 @@ public void testAppsQueryFinalStatus() throws JSONException, Exception { MockNM amNodeManager = rm.registerNode("127.0.0.1:1234", 2048); RMApp app1 = rm.submitApp(CONTAINER_MB); amNodeManager.nodeHeartbeat(true); - WebResource r = resource(); - - ClientResponse response = r.path("ws").path("v1").path("cluster") - .path("apps").queryParam("finalStatus", FinalApplicationStatus.UNDEFINED.toString()) + ClientResponse response = this.constructWebResource("apps") + .queryParam("finalStatus", FinalApplicationStatus.UNDEFINED.toString()) .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); JSONObject json = response.getEntity(JSONObject.class); @@ -483,10 +527,8 @@ public void testAppsQueryFinalStatusNone() throws JSONException, Exception { MockNM amNodeManager = rm.registerNode("127.0.0.1:1234", 2048); rm.submitApp(CONTAINER_MB); amNodeManager.nodeHeartbeat(true); - WebResource r = resource(); - - ClientResponse response = r.path("ws").path("v1").path("cluster") - .path("apps").queryParam("finalStatus", FinalApplicationStatus.KILLED.toString()) + ClientResponse response = this.constructWebResource("apps") + .queryParam("finalStatus", FinalApplicationStatus.KILLED.toString()) .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); JSONObject json = response.getEntity(JSONObject.class); @@ -501,12 +543,11 @@ public void testAppsQueryFinalStatusInvalid() throws JSONException, Exception { MockNM amNodeManager = rm.registerNode("127.0.0.1:1234", 2048); rm.submitApp(CONTAINER_MB); amNodeManager.nodeHeartbeat(true); - WebResource r = resource(); try { - r.path("ws").path("v1").path("cluster").path("apps") - .queryParam("finalStatus", "INVALID_test") - .accept(MediaType.APPLICATION_JSON).get(JSONObject.class); + this.constructWebResource("apps") + .queryParam("finalStatus", "INVALID_test") + .accept(MediaType.APPLICATION_JSON).get(JSONObject.class); fail("should have thrown exception on invalid state query"); } catch (UniformInterfaceException ue) { ClientResponse response = ue.getResponse(); @@ -541,12 +582,8 @@ public void testAppsQueryUser() throws JSONException, Exception { rm.submitApp(CONTAINER_MB); amNodeManager.nodeHeartbeat(true); - WebResource r = resource(); - ClientResponse response = r - .path("ws") - .path("v1") - .path("cluster") - .path("apps") + + ClientResponse response = this.constructWebResource("apps") .queryParam("user", UserGroupInformation.getCurrentUser().getShortUserName()) .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); @@ -569,10 +606,8 @@ public void testAppsQueryQueue() throws JSONException, Exception { rm.submitApp(CONTAINER_MB); amNodeManager.nodeHeartbeat(true); - WebResource r = resource(); - - ClientResponse response = r.path("ws").path("v1").path("cluster") - .path("apps").queryParam("queue", "default") + ClientResponse response = this.constructWebResource("apps") + .queryParam("queue", "default") .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); JSONObject json = response.getEntity(JSONObject.class); @@ -591,9 +626,8 @@ public void testAppsQueryLimit() throws JSONException, Exception { rm.submitApp(CONTAINER_MB); rm.submitApp(CONTAINER_MB); rm.submitApp(CONTAINER_MB); - WebResource r = resource(); - ClientResponse response = r.path("ws").path("v1").path("cluster") - .path("apps").queryParam("limit", "2") + ClientResponse response = this.constructWebResource("apps") + .queryParam("limit", "2") .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); JSONObject json = response.getEntity(JSONObject.class); @@ -614,9 +648,8 @@ public void testAppsQueryStartBegin() throws JSONException, Exception { rm.submitApp(CONTAINER_MB); rm.submitApp(CONTAINER_MB); rm.submitApp(CONTAINER_MB); - WebResource r = resource(); - ClientResponse response = r.path("ws").path("v1").path("cluster") - .path("apps").queryParam("startedTimeBegin", String.valueOf(start)) + ClientResponse response = this.constructWebResource("apps") + .queryParam("startedTimeBegin", String.valueOf(start)) .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); JSONObject json = response.getEntity(JSONObject.class); @@ -637,9 +670,8 @@ public void testAppsQueryStartBeginSome() throws JSONException, Exception { long start = System.currentTimeMillis(); Thread.sleep(1); rm.submitApp(CONTAINER_MB); - WebResource r = resource(); - ClientResponse response = r.path("ws").path("v1").path("cluster") - .path("apps").queryParam("startedTimeBegin", String.valueOf(start)) + ClientResponse response = this.constructWebResource("apps") + .queryParam("startedTimeBegin", String.valueOf(start)) .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); JSONObject json = response.getEntity(JSONObject.class); @@ -660,9 +692,8 @@ public void testAppsQueryStartEnd() throws JSONException, Exception { rm.submitApp(CONTAINER_MB); rm.submitApp(CONTAINER_MB); rm.submitApp(CONTAINER_MB); - WebResource r = resource(); - ClientResponse response = r.path("ws").path("v1").path("cluster") - .path("apps").queryParam("startedTimeEnd", String.valueOf(end)) + ClientResponse response = this.constructWebResource("apps") + .queryParam("startedTimeEnd", String.valueOf(end)) .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); JSONObject json = response.getEntity(JSONObject.class); @@ -682,9 +713,8 @@ public void testAppsQueryStartBeginEnd() throws JSONException, Exception { long end = System.currentTimeMillis(); Thread.sleep(1); rm.submitApp(CONTAINER_MB); - WebResource r = resource(); - ClientResponse response = r.path("ws").path("v1").path("cluster") - .path("apps").queryParam("startedTimeBegin", String.valueOf(start)) + ClientResponse response = this.constructWebResource("apps") + .queryParam("startedTimeBegin", String.valueOf(start)) .queryParam("startedTimeEnd", String.valueOf(end)) .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); @@ -714,10 +744,8 @@ public void testAppsQueryFinishBegin() throws JSONException, Exception { 1, ContainerState.COMPLETE); rm.submitApp(CONTAINER_MB); rm.submitApp(CONTAINER_MB); - - WebResource r = resource(); - ClientResponse response = r.path("ws").path("v1").path("cluster") - .path("apps").queryParam("finishedTimeBegin", String.valueOf(start)) + ClientResponse response = this.constructWebResource("apps") + .queryParam("finishedTimeBegin", String.valueOf(start)) .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); JSONObject json = response.getEntity(JSONObject.class); @@ -747,9 +775,8 @@ public void testAppsQueryFinishEnd() throws JSONException, Exception { rm.submitApp(CONTAINER_MB); long end = System.currentTimeMillis(); - WebResource r = resource(); - ClientResponse response = r.path("ws").path("v1").path("cluster") - .path("apps").queryParam("finishedTimeEnd", String.valueOf(end)) + ClientResponse response = this.constructWebResource("apps") + .queryParam("finishedTimeEnd", String.valueOf(end)) .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); JSONObject json = response.getEntity(JSONObject.class); @@ -781,9 +808,8 @@ public void testAppsQueryFinishBeginEnd() throws JSONException, Exception { rm.submitApp(CONTAINER_MB); long end = System.currentTimeMillis(); - WebResource r = resource(); - ClientResponse response = r.path("ws").path("v1").path("cluster") - .path("apps").queryParam("finishedTimeBegin", String.valueOf(start)) + ClientResponse response = this.constructWebResource("apps") + .queryParam("finishedTimeBegin", String.valueOf(start)) .queryParam("finishedTimeEnd", String.valueOf(end)) .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); @@ -816,9 +842,8 @@ public void testAppsQueryAppTypes() throws JSONException, Exception { rm.submitApp(CONTAINER_MB, "", UserGroupInformation.getCurrentUser() .getShortUserName(), null, false, null, 2, null, "NON-YARN"); - WebResource r = resource(); - ClientResponse response = r.path("ws").path("v1").path("cluster") - .path("apps").queryParam("applicationTypes", "MAPREDUCE") + ClientResponse response = this.constructWebResource("apps") + .queryParam("applicationTypes", "MAPREDUCE") .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); JSONObject json = response.getEntity(JSONObject.class); @@ -830,9 +855,8 @@ public void testAppsQueryAppTypes() throws JSONException, Exception { assertEquals("MAPREDUCE", array.getJSONObject(0).getString("applicationType")); - r = resource(); response = - r.path("ws").path("v1").path("cluster").path("apps") + this.constructWebResource("apps") .queryParam("applicationTypes", "YARN") .queryParam("applicationTypes", "MAPREDUCE") .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); @@ -850,9 +874,8 @@ public void testAppsQueryAppTypes() throws JSONException, Exception { && array.getJSONObject(0).getString("applicationType") .equals("MAPREDUCE"))); - r = resource(); response = - r.path("ws").path("v1").path("cluster").path("apps") + this.constructWebResource("apps") .queryParam("applicationTypes", "YARN,NON-YARN") .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); @@ -869,9 +892,8 @@ public void testAppsQueryAppTypes() throws JSONException, Exception { && array.getJSONObject(0).getString("applicationType") .equals("NON-YARN"))); - r = resource(); - response = r.path("ws").path("v1").path("cluster") - .path("apps").queryParam("applicationTypes", "") + response = this.constructWebResource("apps") + .queryParam("applicationTypes", "") .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); json = response.getEntity(JSONObject.class); @@ -881,9 +903,8 @@ public void testAppsQueryAppTypes() throws JSONException, Exception { array = apps.getJSONArray("app"); assertEquals("incorrect number of elements", 3, array.length()); - r = resource(); response = - r.path("ws").path("v1").path("cluster").path("apps") + this.constructWebResource("apps") .queryParam("applicationTypes", "YARN,NON-YARN") .queryParam("applicationTypes", "MAPREDUCE") .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); @@ -895,9 +916,8 @@ public void testAppsQueryAppTypes() throws JSONException, Exception { array = apps.getJSONArray("app"); assertEquals("incorrect number of elements", 3, array.length()); - r = resource(); response = - r.path("ws").path("v1").path("cluster").path("apps") + this.constructWebResource("apps") .queryParam("applicationTypes", "YARN") .queryParam("applicationTypes", "") .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); @@ -911,9 +931,8 @@ public void testAppsQueryAppTypes() throws JSONException, Exception { assertEquals("YARN", array.getJSONObject(0).getString("applicationType")); - r = resource(); response = - r.path("ws").path("v1").path("cluster").path("apps") + this.constructWebResource("apps") .queryParam("applicationTypes", ",,, ,, YARN ,, ,") .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); @@ -926,9 +945,8 @@ public void testAppsQueryAppTypes() throws JSONException, Exception { assertEquals("YARN", array.getJSONObject(0).getString("applicationType")); - r = resource(); response = - r.path("ws").path("v1").path("cluster").path("apps") + this.constructWebResource("apps") .queryParam("applicationTypes", ",,, ,, ,, ,") .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); @@ -939,9 +957,8 @@ public void testAppsQueryAppTypes() throws JSONException, Exception { array = apps.getJSONArray("app"); assertEquals("incorrect number of elements", 3, array.length()); - r = resource(); response = - r.path("ws").path("v1").path("cluster").path("apps") + this.constructWebResource("apps") .queryParam("applicationTypes", "YARN, ,NON-YARN, ,,") .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); @@ -958,9 +975,8 @@ public void testAppsQueryAppTypes() throws JSONException, Exception { && array.getJSONObject(0).getString("applicationType") .equals("NON-YARN"))); - r = resource(); response = - r.path("ws").path("v1").path("cluster").path("apps") + this.constructWebResource("apps") .queryParam("applicationTypes", " YARN, , ,,,") .queryParam("applicationTypes", "MAPREDUCE , ,, ,") .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); @@ -1004,9 +1020,7 @@ public void testAppStatistics() throws JSONException, Exception { .getShortUserName(), null, false, null, 2, null, "OTHER"); // zero type, zero state - WebResource r = resource(); - ClientResponse response = r.path("ws").path("v1").path("cluster") - .path("appstatistics") + ClientResponse response = this.constructWebResource("appstatistics") .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); JSONObject json = response.getEntity(JSONObject.class); @@ -1029,9 +1043,7 @@ public void testAppStatistics() throws JSONException, Exception { } // zero type, one state - r = resource(); - response = r.path("ws").path("v1").path("cluster") - .path("appstatistics") + response = this.constructWebResource("appstatistics") .queryParam("states", YarnApplicationState.ACCEPTED.toString()) .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); @@ -1046,9 +1058,7 @@ public void testAppStatistics() throws JSONException, Exception { assertEquals("2", statItems.getJSONObject(0).getString("count")); // one type, zero state - r = resource(); - response = r.path("ws").path("v1").path("cluster") - .path("appstatistics") + response = this.constructWebResource("appstatistics") .queryParam("applicationTypes", "MAPREDUCE") .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); @@ -1072,9 +1082,7 @@ public void testAppStatistics() throws JSONException, Exception { } // two types, zero state - r = resource(); - response = r.path("ws").path("v1").path("cluster") - .path("appstatistics") + response = this.constructWebResource("appstatistics") .queryParam("applicationTypes", "MAPREDUCE,OTHER") .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); assertEquals(Status.BAD_REQUEST, response.getClientResponseStatus()); @@ -1094,9 +1102,7 @@ public void testAppStatistics() throws JSONException, Exception { "org.apache.hadoop.yarn.webapp.BadRequestException", className); // one type, two states - r = resource(); - response = r.path("ws").path("v1").path("cluster") - .path("appstatistics") + response = this.constructWebResource("appstatistics") .queryParam("states", YarnApplicationState.FINISHED.toString() + "," + YarnApplicationState.ACCEPTED.toString()) .queryParam("applicationTypes", "MAPREDUCE") @@ -1120,9 +1126,8 @@ public void testAppStatistics() throws JSONException, Exception { assertEquals("1", statItem2.getString("count")); // invalid state - r = resource(); - response = r.path("ws").path("v1").path("cluster") - .path("appstatistics").queryParam("states", "wrong_state") + response = this.constructWebResource("appstatistics") + .queryParam("states", "wrong_state") .accept(MediaType.APPLICATION_JSON).get(ClientResponse.class); assertEquals(Status.BAD_REQUEST, response.getClientResponseStatus()); assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); @@ -1182,12 +1187,11 @@ public void testInvalidApp() throws JSONException, Exception { MockNM amNodeManager = rm.registerNode("127.0.0.1:1234", 2048); rm.submitApp(CONTAINER_MB); amNodeManager.nodeHeartbeat(true); - WebResource r = resource(); try { - r.path("ws").path("v1").path("cluster").path("apps") - .path("application_invalid_12").accept(MediaType.APPLICATION_JSON) - .get(JSONObject.class); + this.constructWebResource("apps", "application_invalid_12") + .accept(MediaType.APPLICATION_JSON) + .get(JSONObject.class); fail("should have thrown exception on invalid appid"); } catch (UniformInterfaceException ue) { ClientResponse response = ue.getResponse(); @@ -1218,12 +1222,11 @@ public void testNonexistApp() throws JSONException, Exception { MockNM amNodeManager = rm.registerNode("127.0.0.1:1234", 2048); rm.submitApp(CONTAINER_MB, "testwordcount", "user1"); amNodeManager.nodeHeartbeat(true); - WebResource r = resource(); try { - r.path("ws").path("v1").path("cluster").path("apps") - .path("application_00000_0099").accept(MediaType.APPLICATION_JSON) - .get(JSONObject.class); + this.constructWebResource("apps", "application_00000_0099") + .accept(MediaType.APPLICATION_JSON) + .get(JSONObject.class); fail("should have thrown exception on invalid appid"); } catch (UniformInterfaceException ue) { ClientResponse response = ue.getResponse(); @@ -1251,9 +1254,8 @@ public void testNonexistApp() throws JSONException, Exception { public void testSingleAppsHelper(String path, RMApp app, String media) throws JSONException, Exception { - WebResource r = resource(); - ClientResponse response = r.path("ws").path("v1").path("cluster") - .path("apps").path(path).accept(media).get(ClientResponse.class); + ClientResponse response = this.constructWebResource("apps", path) + .accept(media).get(ClientResponse.class); assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); JSONObject json = response.getEntity(JSONObject.class); @@ -1267,9 +1269,8 @@ public void testSingleAppsXML() throws JSONException, Exception { MockNM amNodeManager = rm.registerNode("127.0.0.1:1234", 2048); RMApp app1 = rm.submitApp(CONTAINER_MB, "testwordcount", "user1"); amNodeManager.nodeHeartbeat(true); - WebResource r = resource(); - ClientResponse response = r.path("ws").path("v1").path("cluster") - .path("apps").path(app1.getApplicationId().toString()) + ClientResponse response = this.constructWebResource("apps", + app1.getApplicationId().toString()) .accept(MediaType.APPLICATION_XML).get(ClientResponse.class); assertEquals(MediaType.APPLICATION_XML_TYPE, response.getType()); String xml = response.getEntity(String.class); @@ -1442,12 +1443,11 @@ public void testInvalidAppAttempts() throws JSONException, Exception { MockNM amNodeManager = rm.registerNode("127.0.0.1:1234", 2048); rm.submitApp(CONTAINER_MB); amNodeManager.nodeHeartbeat(true); - WebResource r = resource(); try { - r.path("ws").path("v1").path("cluster").path("apps") - .path("application_invalid_12").accept(MediaType.APPLICATION_JSON) - .get(JSONObject.class); + this.constructWebResource("apps", "application_invalid_12") + .accept(MediaType.APPLICATION_JSON) + .get(JSONObject.class); fail("should have thrown exception on invalid appid"); } catch (UniformInterfaceException ue) { ClientResponse response = ue.getResponse(); @@ -1478,12 +1478,11 @@ public void testNonexistAppAttempts() throws JSONException, Exception { MockNM amNodeManager = rm.registerNode("127.0.0.1:1234", 2048); rm.submitApp(CONTAINER_MB, "testwordcount", "user1"); amNodeManager.nodeHeartbeat(true); - WebResource r = resource(); try { - r.path("ws").path("v1").path("cluster").path("apps") - .path("application_00000_0099").accept(MediaType.APPLICATION_JSON) - .get(JSONObject.class); + this.constructWebResource("apps", "application_00000_0099") + .accept(MediaType.APPLICATION_JSON) + .get(JSONObject.class); fail("should have thrown exception on invalid appid"); } catch (UniformInterfaceException ue) { ClientResponse response = ue.getResponse(); @@ -1511,9 +1510,8 @@ public void testNonexistAppAttempts() throws JSONException, Exception { public void testAppAttemptsHelper(String path, RMApp app, String media) throws JSONException, Exception { - WebResource r = resource(); - ClientResponse response = r.path("ws").path("v1").path("cluster") - .path("apps").path(path).path("appattempts").accept(media) + ClientResponse response = this.constructWebResource("apps", path, "appattempts") + .accept(media) .get(ClientResponse.class); assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); JSONObject json = response.getEntity(JSONObject.class); @@ -1541,10 +1539,9 @@ public void testAppAttemptsXML() throws JSONException, Exception { MockNM amNodeManager = rm.registerNode("127.0.0.1:1234", 2048); RMApp app1 = rm.submitApp(CONTAINER_MB, "testwordcount", user); amNodeManager.nodeHeartbeat(true); - WebResource r = resource(); - ClientResponse response = r.path("ws").path("v1").path("cluster") - .path("apps").path(app1.getApplicationId().toString()) - .path("appattempts").accept(MediaType.APPLICATION_XML) + ClientResponse response = this.constructWebResource("apps", + app1.getApplicationId().toString(), "appattempts") + .accept(MediaType.APPLICATION_XML) .get(ClientResponse.class); assertEquals(MediaType.APPLICATION_XML_TYPE, response.getType()); String xml = response.getEntity(String.class); @@ -1611,6 +1608,168 @@ public void verifyAppAttemptInfoGeneric(RMAppAttempt appAttempt, int id, "logsLink doesn't contain user info", logsLink.endsWith("/" + user)); } - + + private WebResource constructWebResource(WebResource r, String... paths) { + WebResource rt = r; + for(String path: paths) { + rt = rt.path(path); + } + if(rm.getConfig().getBoolean(YarnConfiguration.YARN_ACL_ENABLE, false) == true) { + rt = rt.queryParam("user.name", webserviceUserName); + } + return rt; + } + + private WebResource constructWebResource(String... paths) { + WebResource r = resource(); + WebResource ws = r.path("ws").path("v1").path("cluster"); + return this.constructWebResource(ws, paths); + } + + @Test (timeout = 90000) + public void testSingleAppKill() throws Exception { + rm.start(); + MockNM amNodeManager = rm.registerNode("127.0.0.1:1234", 2048); + + String[] mediaTypes = {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}; + for(String mediaType: mediaTypes) { + RMApp app = rm.submitApp(CONTAINER_MB, "", webserviceUserName); + amNodeManager.nodeHeartbeat(true); + ClientResponse response = this.constructWebResource("apps", + app.getApplicationId().toString(), "kill") + .accept(mediaType) + .put(ClientResponse.class); + + if(rm.getConfig().getBoolean(YarnConfiguration.YARN_ACL_ENABLE, false) == false) { + assertEquals(Status.UNAUTHORIZED, response.getClientResponseStatus()); + return; + } + assertEquals(Status.ACCEPTED, response.getClientResponseStatus()); + if(mediaType == MediaType.APPLICATION_JSON) { + verifyAppKillJson(response, RMAppState.ACCEPTED); + } + else { + verifyAppKillXML(response, RMAppState.ACCEPTED); + } + + String locationHeaderValue = response.getHeaders().getFirst("Location"); + Client c = Client.create(); + WebResource tmp = c.resource(locationHeaderValue); + if(rm.getConfig().getBoolean(YarnConfiguration.YARN_ACL_ENABLE, false) == true) { + tmp = tmp.queryParam("user.name", webserviceUserName); + } + response = tmp.get(ClientResponse.class); + assertEquals(Status.OK, response.getClientResponseStatus()); + assertTrue(locationHeaderValue.endsWith("/ws/v1/cluster/apps/" + app.getApplicationId().toString())); + + while(true) { + Thread.sleep(1000); + response = this.constructWebResource("apps", app.getApplicationId().toString(), "kill") + .accept(mediaType) + .put(ClientResponse.class); + assertTrue((response.getClientResponseStatus() == Status.ACCEPTED) || + (response.getClientResponseStatus() == Status.OK)); + if(response.getClientResponseStatus() == Status.OK) { + if(mediaType == MediaType.APPLICATION_JSON) { + verifyAppKillJson(response, RMAppState.KILLED); + } + else { + verifyAppKillXML(response, RMAppState.KILLED); + } + break; + } + } + } + + rm.stop(); + return; + } + + protected static void verifyAppKillJson(ClientResponse response, RMAppState state) throws JSONException { + + assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); + JSONObject json = response.getEntity(JSONObject.class); + assertEquals("incorrect number of elements", 1, json.length()); + JSONObject appObj = json.getJSONObject("app"); + assertEquals("app state incorrect", + state.toString(), appObj.getString("state")); + return; + } + + protected static void verifyAppKillXML(ClientResponse response, RMAppState appState) + throws ParserConfigurationException, IOException, SAXException{ + assertEquals(MediaType.APPLICATION_XML_TYPE, response.getType()); + String xml = response.getEntity(String.class); + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder db = dbf.newDocumentBuilder(); + InputSource is = new InputSource(); + is.setCharacterStream(new StringReader(xml)); + Document dom = db.parse(is); + NodeList nodes = dom.getElementsByTagName("app"); + assertEquals("incorrect number of elements", 1, nodes.getLength()); + Element element = (Element) nodes.item(0); + String state = WebServicesTestUtils.getXmlString(element, "state"); + assertEquals("app state incorrect", appState.toString(), state); + return; + } + + @Test (timeout = 30000) + public void testSingleAppKillUnauthorized() throws Exception { + rm.start(); + MockNM amNodeManager = rm.registerNode("127.0.0.1:1234", 2048); + + String[] mediaTypes = {MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}; + for(String mediaType: mediaTypes) { + RMApp app = rm.submitApp(CONTAINER_MB, "test", "someuser"); + amNodeManager.nodeHeartbeat(true); + ClientResponse response = this.constructWebResource("apps", + app.getApplicationId().toString(), "kill") + .accept(mediaType) + .put(ClientResponse.class); + if(rm.getConfig().getBoolean(YarnConfiguration.YARN_ACL_ENABLE, false) == false) { + assertEquals(Status.UNAUTHORIZED, response.getClientResponseStatus()); + } + else { + assertEquals(Status.FORBIDDEN, response.getClientResponseStatus()); + } + } + + // there's a condition where if the rm is stopped while an app + // is being killed, it can lead to the test timing out, hence the sleep + Thread.sleep(3000); + + rm.stop(); + return; + + } + + @Test + public void testSingleAppKillInvalidId() throws Exception { + rm.start(); + MockNM amNodeManager = rm.registerNode("127.0.0.1:1234", 2048); + amNodeManager.nodeHeartbeat(true); + String[] testAppIds = {"application_1391705042196_0001", "random_string"}; + for(String testAppId: testAppIds) { + ClientResponse response = this.constructWebResource("apps", testAppId, "kill") + .accept(MediaType.APPLICATION_XML) + .put(ClientResponse.class); + if(rm.getConfig().getBoolean(YarnConfiguration.YARN_ACL_ENABLE, false) == false) { + assertEquals(Status.UNAUTHORIZED, response.getClientResponseStatus()); + continue; + } + assertEquals(Status.NOT_FOUND, response.getClientResponseStatus()); + } + rm.stop(); + return; + } + + @After + @Override + public void tearDown() throws Exception { + if(rm != null) { + rm.stop(); + } + super.tearDown(); + } }