diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/RMWebServices.java 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 a8ec192..c370844 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/RMWebServices.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/RMWebServices.java @@ -79,6 +79,7 @@ import org.apache.hadoop.yarn.api.protocolrecords.GetDelegationTokenResponse; import org.apache.hadoop.yarn.api.protocolrecords.RenewDelegationTokenRequest; import org.apache.hadoop.yarn.api.protocolrecords.RenewDelegationTokenResponse; +import org.apache.hadoop.yarn.api.protocolrecords.MoveApplicationAcrossQueuesRequest; import org.apache.hadoop.yarn.api.records.ApplicationAccessType; import org.apache.hadoop.yarn.api.records.ApplicationId; import org.apache.hadoop.yarn.api.records.ApplicationReport; @@ -115,6 +116,7 @@ import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.NewApplication; import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.AppInfo; import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.AppState; +import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.AppQueue; import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ApplicationSubmissionContextInfo; import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ApplicationStatisticsInfo; import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.AppsInfo; @@ -692,7 +694,7 @@ public Response updateAppState(AppState targetState, app = getRMAppForAppId(appId); } catch (NotFoundException e) { RMAuditLogger.logFailure(userName, AuditConstants.KILL_APP_REQUEST, - "UNKNOWN", "RMWebService", "Trying to kill/move an absent application " + "UNKNOWN", "RMWebService", "Trying to kill an absent application " + appId); throw e; } @@ -768,6 +770,126 @@ public KillApplicationResponse run() throws IOException, return Response.status(Status.OK).entity(ret).build(); } + @GET + @Path("/apps/{appid}/queue") + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + public AppQueue getAppQueue(@Context HttpServletRequest hsr, + @PathParam("appid") String appId) throws AuthorizationException { + init(); + UserGroupInformation callerUGI = getCallerUserGroupInformation(hsr, true); + String userName = ""; + if (callerUGI != null) { + userName = callerUGI.getUserName(); + } + RMApp app = null; + try { + app = getRMAppForAppId(appId); + } catch (NotFoundException e) { + RMAuditLogger.logFailure(userName, AuditConstants.KILL_APP_REQUEST, + "UNKNOWN", "RMWebService", + "Trying to get state of an absent application " + appId); + throw e; + } + + AppQueue ret = new AppQueue(); + ret.setQueue(app.getQueue()); + + return ret; + } + + @PUT + @Path("/apps/{appid}/queue") + @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + @Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }) + public Response updateAppQueue(AppQueue targetQueue, + @Context HttpServletRequest hsr, @PathParam("appid") String appId) + throws AuthorizationException, YarnException, InterruptedException, + IOException { + + init(); + UserGroupInformation callerUGI = getCallerUserGroupInformation(hsr, true); + if (callerUGI == null) { + String msg = "Unable to obtain user name, user not authenticated"; + throw new AuthorizationException(msg); + } + + if (UserGroupInformation.isSecurityEnabled() && isStaticUser(callerUGI)) { + String msg = "The default static user cannot carry out this operation."; + return Response.status(Status.FORBIDDEN).entity(msg).build(); + } + + String userName = callerUGI.getUserName(); + RMApp app = null; + try { + app = getRMAppForAppId(appId); + } catch (NotFoundException e) { + RMAuditLogger.logFailure(userName, AuditConstants.KILL_APP_REQUEST, + "UNKNOWN", "RMWebService", "Trying to move an absent application " + + appId); + throw e; + } + + if (!app.getQueue().equals(targetQueue.getQueue())) { + // user is attempting to change queue. + return moveApp(app, callerUGI, targetQueue.getQueue()); + } + + AppQueue ret = new AppQueue(); + ret.setQueue(app.getQueue()); + + return Response.status(Status.OK).entity(ret).build(); + } + + protected Response moveApp(RMApp app, UserGroupInformation callerUGI, + String targetQueue) throws IOException, InterruptedException { + + if (app == null) { + throw new IllegalArgumentException("app cannot be null"); + } + String userName = callerUGI.getUserName(); + final ApplicationId appid = app.getApplicationId(); + final String reqTargetQueue = targetQueue; + try { + callerUGI + .doAs(new PrivilegedExceptionAction() { + @Override + public Void run() throws IOException, + YarnException { + MoveApplicationAcrossQueuesRequest req = + MoveApplicationAcrossQueuesRequest.newInstance(appid, + reqTargetQueue); + rm.getClientRMService().moveApplicationAcrossQueues(req); + return null; + } + }); + } catch (UndeclaredThrowableException ue) { + // if the root cause is a permissions issue + // bubble that up to the user + if (ue.getCause() instanceof YarnException) { + YarnException ye = (YarnException) ue.getCause(); + if (ye.getCause() instanceof AccessControlException) { + String appId = app.getApplicationId().toString(); + String msg = + "Unauthorized attempt to move appid " + appId + + " by remote user " + userName; + return Response.status(Status.FORBIDDEN).entity(msg).build(); + } else if (ye.getMessage().startsWith("App in") + && ye.getMessage().endsWith("state cannot be moved.")) { + return Response.status(Status.BAD_REQUEST).entity(ye.getMessage()) + .build(); + } else { + throw ue; + } + } else { + throw ue; + } + } + + AppQueue ret = new AppQueue(); + ret.setQueue(app.getQueue()); + return Response.status(Status.OK).entity(ret).build(); + } + private RMApp getRMAppForAppId(String appId) { if (appId == null || appId.isEmpty()) { diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/AppQueue.java hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/AppQueue.java new file mode 100644 index 0000000..5dab6ce --- /dev/null +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/main/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/dao/AppQueue.java @@ -0,0 +1,46 @@ +/** + * 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.dao; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement(name = "appqueue") +@XmlAccessorType(XmlAccessType.FIELD) +public class AppQueue { + + String queue; + + public AppQueue() { + } + + public AppQueue(String queue) { + this.queue = queue; + } + + public void setQueue(String queue) { + this.queue = queue; + } + + public String getQueue() { + return this.queue; + } + +} diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesAppsModification.java 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 12c5686..ce44b58 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesAppsModification.java +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-resourcemanager/src/test/java/org/apache/hadoop/yarn/server/resourcemanager/webapp/TestRMWebServicesAppsModification.java @@ -66,10 +66,7 @@ import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacityScheduler; import org.apache.hadoop.yarn.server.resourcemanager.scheduler.capacity.CapacitySchedulerConfiguration; import org.apache.hadoop.yarn.server.resourcemanager.security.QueueACLsManager; -import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.AppState; -import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ApplicationSubmissionContextInfo; -import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.CredentialsInfo; -import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.LocalResourceInfo; +import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.*; import org.apache.hadoop.yarn.server.security.ApplicationACLsManager; import org.apache.hadoop.yarn.util.ConverterUtils; import org.apache.hadoop.yarn.webapp.GenericExceptionHandler; @@ -77,6 +74,7 @@ import org.codehaus.jettison.json.JSONException; import org.codehaus.jettison.json.JSONObject; import org.junit.After; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -261,9 +259,9 @@ public void testSingleAppState() throws Exception { .constructWebResource("apps", app.getApplicationId().toString(), "state").accept(mediaType).get(ClientResponse.class); assertEquals(Status.OK, response.getClientResponseStatus()); - if (mediaType == MediaType.APPLICATION_JSON) { + if (mediaType.equals(MediaType.APPLICATION_JSON)) { verifyAppStateJson(response, RMAppState.ACCEPTED); - } else if (mediaType == MediaType.APPLICATION_XML) { + } else if (mediaType.equals(MediaType.APPLICATION_XML)) { verifyAppStateXML(response, RMAppState.ACCEPTED); } } @@ -282,21 +280,16 @@ public void testSingleAppKill() throws Exception { for (MediaType contentType : contentTypes) { RMApp app = rm.submitApp(CONTAINER_MB, "", webserviceUserName); amNodeManager.nodeHeartbeat(true); - - ClientResponse response = - this - .constructWebResource("apps", app.getApplicationId().toString(), - "state").accept(mediaType).get(ClientResponse.class); AppState targetState = new AppState(YarnApplicationState.KILLED.toString()); Object entity; - if (contentType == MediaType.APPLICATION_JSON_TYPE) { + if (contentType.equals(MediaType.APPLICATION_JSON_TYPE)) { entity = appStateToJSON(targetState); } else { entity = targetState; } - response = + ClientResponse response = this .constructWebResource("apps", app.getApplicationId().toString(), "state").entity(entity, contentType).accept(mediaType) @@ -307,7 +300,7 @@ public void testSingleAppKill() throws Exception { continue; } assertEquals(Status.ACCEPTED, response.getClientResponseStatus()); - if (mediaType == MediaType.APPLICATION_JSON) { + if (mediaType.equals(MediaType.APPLICATION_JSON)) { verifyAppStateJson(response, RMAppState.KILLING, RMAppState.ACCEPTED); } else { verifyAppStateXML(response, RMAppState.KILLING, RMAppState.ACCEPTED); @@ -336,7 +329,7 @@ public void testSingleAppKill() throws Exception { || (response.getClientResponseStatus() == Status.OK)); if (response.getClientResponseStatus() == Status.OK) { assertEquals(RMAppState.KILLED, app.getState()); - if (mediaType == MediaType.APPLICATION_JSON) { + if (mediaType.equals(MediaType.APPLICATION_JSON)) { verifyAppStateJson(response, RMAppState.KILLED); } else { verifyAppStateXML(response, RMAppState.KILLED); @@ -348,7 +341,6 @@ public void testSingleAppKill() throws Exception { } rm.stop(); - return; } @Test @@ -371,7 +363,7 @@ public void testSingleAppKillInvalidState() throws Exception { ClientResponse response; AppState targetState = new AppState(targetStateString); Object entity; - if (contentType == MediaType.APPLICATION_JSON_TYPE) { + if (contentType.equals(MediaType.APPLICATION_JSON_TYPE)) { entity = appStateToJSON(targetState); } else { entity = targetState; @@ -394,7 +386,6 @@ public void testSingleAppKillInvalidState() throws Exception { } rm.stop(); - return; } private static String appStateToJSON(AppState state) throws Exception { @@ -420,7 +411,6 @@ protected static void verifyAppStateJson(ClientResponse response, } String msg = "app state incorrect, got " + responseState; assertTrue(msg, valid); - return; } protected static void verifyAppStateXML(ClientResponse response, @@ -445,7 +435,6 @@ protected static void verifyAppStateXML(ClientResponse response, } String msg = "app state incorrect, got " + state; assertTrue(msg, valid); - return; } @Test(timeout = 30000) @@ -485,8 +474,6 @@ public void testSingleAppKillUnauthorized() throws Exception { validateResponseStatus(response, Status.FORBIDDEN); } rm.stop(); - return; - } @Test @@ -508,7 +495,6 @@ public void testSingleAppKillInvalidId() throws Exception { assertEquals(Status.NOT_FOUND, response.getClientResponseStatus()); } rm.stop(); - return; } @After @@ -569,7 +555,6 @@ public void testGetNewApplication() throws Exception { testGetNewApplication(acceptMedia); } rm.stop(); - return; } protected String testGetNewApplication(String mediaType) throws JSONException, @@ -604,7 +589,7 @@ protected String validateGetNewApplicationResponse(ClientResponse resp) protected String validateGetNewApplicationJsonResponse(JSONObject json) throws JSONException { String appId = json.getString("application-id"); - assertTrue(appId.isEmpty() == false); + assertTrue(!appId.isEmpty()); JSONObject maxResources = json.getJSONObject("maximum-resource-capability"); long memory = maxResources.getLong("memory"); long vCores = maxResources.getLong("vCores"); @@ -624,7 +609,7 @@ protected String validateGetNewApplicationXMLResponse(String response) assertEquals("incorrect number of elements", 1, nodes.getLength()); Element element = (Element) nodes.item(0); String appId = WebServicesTestUtils.getXmlString(element, "application-id"); - assertTrue(appId.isEmpty() == false); + assertTrue(!appId.isEmpty()); NodeList maxResourceNodes = element.getElementsByTagName("maximum-resource-capability"); assertEquals(1, maxResourceNodes.getLength()); @@ -654,7 +639,6 @@ public void testGetNewApplicationAndSubmit() throws Exception { } } rm.stop(); - return; } public void testAppSubmit(String acceptMedia, String contentMedia) @@ -718,14 +702,14 @@ public void testAppSubmit(String acceptMedia, String contentMedia) this.constructWebResource(urlPath).accept(acceptMedia) .entity(appInfo, contentMedia).post(ClientResponse.class); - if (this.isAuthenticationEnabled() == false) { + if (!this.isAuthenticationEnabled()) { assertEquals(Status.UNAUTHORIZED, response.getClientResponseStatus()); return; } assertEquals(Status.ACCEPTED, response.getClientResponseStatus()); - assertTrue(response.getHeaders().getFirst(HttpHeaders.LOCATION).isEmpty() == false); + assertTrue(!response.getHeaders().getFirst(HttpHeaders.LOCATION).isEmpty()); String locURL = response.getHeaders().getFirst(HttpHeaders.LOCATION); - assertTrue(locURL.indexOf("/apps/application") != -1); + assertTrue(locURL.contains("/apps/application")); appId = locURL.substring(locURL.indexOf("/apps/") + "/apps/".length()); WebResource res = resource().uri(new URI(locURL)); @@ -762,7 +746,6 @@ public void testAppSubmit(String acceptMedia, String contentMedia) this.constructWebResource("apps", appId).accept(acceptMedia) .get(ClientResponse.class); assertEquals(Status.OK, response.getClientResponseStatus()); - return; } public void testAppSubmitErrors(String acceptMedia, String contentMedia) @@ -772,14 +755,13 @@ public void testAppSubmitErrors(String acceptMedia, String contentMedia) // REST API and make sure we get the right error response codes String urlPath = "apps"; - String appId = ""; ApplicationSubmissionContextInfo appInfo = new ApplicationSubmissionContextInfo(); ClientResponse response = this.constructWebResource(urlPath).accept(acceptMedia) .entity(appInfo, contentMedia).post(ClientResponse.class); validateResponseStatus(response, Status.BAD_REQUEST); - appId = "random"; + String appId = "random"; appInfo.setApplicationId(appId); response = this.constructWebResource(urlPath).accept(acceptMedia) @@ -814,8 +796,6 @@ public void testAppSubmitErrors(String acceptMedia, String contentMedia) this.constructWebResource(urlPath).accept(acceptMedia) .entity(appInfo, contentMedia).post(ClientResponse.class); validateResponseStatus(response, Status.BAD_REQUEST); - - return; } @Test @@ -863,4 +843,136 @@ public void testAppSubmitBadJsonAndXML() throws Exception { rm.stop(); } + @Test + public void testGetAppQueue() throws Exception { + client().addFilter(new LoggingFilter(System.out)); + rm.start(); + MockNM amNodeManager = rm.registerNode("127.0.0.1:1234", 2048); + String[] contentTypes = + { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }; + for (String contentType : contentTypes) { + RMApp app = rm.submitApp(CONTAINER_MB, "", webserviceUserName); + amNodeManager.nodeHeartbeat(true); + ClientResponse response = + this + .constructWebResource("apps", app.getApplicationId().toString(), + "queue").accept(contentType).get(ClientResponse.class); + assertEquals(Status.OK, response.getClientResponseStatus()); + if (contentType.equals(MediaType.APPLICATION_JSON)) { + verifyAppQueueJson(response, "default"); + } else { + verifyAppQueueXML(response, "default"); + } + } + rm.stop(); + } + + @Test(timeout = 90000) + public void testAppMove() throws Exception { + + client().addFilter(new LoggingFilter(System.out)); + + boolean isCapacityScheduler = + rm.getResourceScheduler() instanceof CapacityScheduler; + assumeTrue("Currently this test is only supported on CapacityScheduler", + isCapacityScheduler); + + // default root queue allows anyone to have admin acl + CapacitySchedulerConfiguration csconf = + new CapacitySchedulerConfiguration(); + String[] queues = { "default", "test" }; + csconf.setQueues("root", queues); + csconf.setCapacity("root.default", 50.0f); + csconf.setCapacity("root.test", 50.0f); + csconf.setAcl("root", QueueACL.ADMINISTER_QUEUE, "someuser"); + csconf.setAcl("root.default", QueueACL.ADMINISTER_QUEUE, "someuser"); + csconf.setAcl("root.test", QueueACL.ADMINISTER_QUEUE, "someuser"); + rm.getResourceScheduler().reinitialize(csconf, rm.getRMContext()); + + rm.start(); + MockNM amNodeManager = rm.registerNode("127.0.0.1:1234", 2048); + String[] mediaTypes = + { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML }; + MediaType[] contentTypes = + { MediaType.APPLICATION_JSON_TYPE, MediaType.APPLICATION_XML_TYPE }; + for (String mediaType : mediaTypes) { + for (MediaType contentType : contentTypes) { + RMApp app = rm.submitApp(CONTAINER_MB, "", webserviceUserName); + amNodeManager.nodeHeartbeat(true); + AppQueue targetQueue = new AppQueue("test"); + Object entity; + if (contentType.equals(MediaType.APPLICATION_JSON_TYPE)) { + entity = appQueueToJSON(targetQueue); + } else { + entity = targetQueue; + } + ClientResponse response = + this + .constructWebResource("apps", app.getApplicationId().toString(), + "queue").entity(entity, contentType).accept(mediaType) + .put(ClientResponse.class); + + if (!isAuthenticationEnabled()) { + assertEquals(Status.UNAUTHORIZED, response.getClientResponseStatus()); + continue; + } + assertEquals(Status.OK, response.getClientResponseStatus()); + if (mediaType.equals(MediaType.APPLICATION_JSON)) { + verifyAppQueueJson(response, "test"); + } else { + verifyAppQueueXML(response, "test"); + } + Assert.assertEquals("test", app.getQueue()); + + // check unauthorized + app = rm.submitApp(CONTAINER_MB, "", "someuser"); + amNodeManager.nodeHeartbeat(true); + response = + this + .constructWebResource("apps", app.getApplicationId().toString(), + "queue").entity(entity, contentType).accept(mediaType) + .put(ClientResponse.class); + assertEquals(Status.FORBIDDEN, response.getClientResponseStatus()); + Assert.assertEquals("default", app.getQueue()); + } + } + rm.stop(); + } + + String appQueueToJSON(AppQueue targetQueue) throws Exception { + StringWriter sw = new StringWriter(); + JSONJAXBContext ctx = new JSONJAXBContext(AppQueue.class); + JSONMarshaller jm = ctx.createJSONMarshaller(); + jm.marshallToJSON(targetQueue, sw); + return sw.toString(); + } + + protected static void + verifyAppQueueJson(ClientResponse response, String queue) + throws JSONException { + + assertEquals(MediaType.APPLICATION_JSON_TYPE, response.getType()); + JSONObject json = response.getEntity(JSONObject.class); + assertEquals("incorrect number of elements", 1, json.length()); + String responseQueue = json.getString("queue"); + assertEquals(queue, responseQueue); + } + + protected static void + verifyAppQueueXML(ClientResponse response, String queue) + 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("appqueue"); + assertEquals("incorrect number of elements", 1, nodes.getLength()); + Element element = (Element) nodes.item(0); + String responseQueue = WebServicesTestUtils.getXmlString(element, "queue"); + assertEquals(queue, responseQueue); + } + } diff --git hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/apt/ResourceManagerRest.apt.vm hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/apt/ResourceManagerRest.apt.vm index 9609ba3..5766a18 100644 --- hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/apt/ResourceManagerRest.apt.vm +++ hadoop-yarn-project/hadoop-yarn/hadoop-yarn-site/src/site/apt/ResourceManagerRest.apt.vm @@ -2579,7 +2579,7 @@ Server: Jetty(6.1.26) +---+ HTTP/1.1 202 Accepted -Content-Type: application/json +Content-Type: application/xml Content-Length: 794 Location: http:///ws/v1/cluster/apps/application_1399397633663_0003 Server: Jetty(6.1.26) @@ -2648,8 +2648,6 @@ Server: Jetty(6.1.26) +---+ HTTP/1.1 403 Unauthorized -Content-Type: application/json -Transfer-Encoding: chunked Server: Jetty(6.1.26) +---+ @@ -2692,6 +2690,161 @@ Server: Jetty(6.1.26) +---+ +* Cluster Application Queue API + + With the application queue API, you can query the queue of a submitted app as well move a running app to another queue using a PUT request specifying the target queue. To perform the PUT operation, authentication has to be setup for the RM web services. In addition, you must be authorized to move the app. Currently you can only move the app if you're using the Capacity scheduler or the Fair scheduler. + + Please note that in order to move an app, you must have an authentication filter setup for the HTTP interface. The functionality requires that a username is set in the HttpServletRequest. If no filter is setup, the response will be an "UNAUTHORIZED" response. + + This feature is currently in the alpha stage and may change in the future. + +** URI + +----- + * http:///ws/v1/cluster/apps/{appid}/queue +----- + +** HTTP Operations Supported + +------ + * GET + * PUT +------ + +** Query Parameters Supported + +------ + None +------ + +** Elements of object + + When you make a request for the state of an app, the information returned has the following fields + +*---------------+--------------+-------------------------------+ +|| Item || Data Type || Description | +*---------------+--------------+-------------------------------+ +| queue | string | The application queue | +*---------------+--------------+--------------------------------+ + + +** Response Examples + + <> + + HTTP Request + +----- + GET http:///ws/v1/cluster/apps/application_1399397633663_0003/queue +----- + + Response Header: + ++---+ +HTTP/1.1 200 OK +Content-Type: application/json +Transfer-Encoding: chunked +Server: Jetty(6.1.26) ++---+ + + Response Body: + ++---+ +{ + "queue":"default" +} ++---+ + + HTTP Request + +----- + PUT http:///ws/v1/cluster/apps/application_1399397633663_0003/queue +---- + + Request Body: + ++---+ +{ + "queue":"test" +} ++---+ + + Response Header: + ++---+ +HTTP/1.1 200 OK +Content-Type: application/json +Transfer-Encoding: chunked +Server: Jetty(6.1.26) ++---+ + + Response Body: + ++---+ +{ + "queue":"test" +} ++---+ + + <> + + HTTP Request + +----- + GET http:///ws/v1/cluster/apps/application_1399397633663_0003/queue +----- + + Response Header: + ++---+ +HTTP/1.1 200 OK +Content-Type: application/xml +Content-Length: 98 +Server: Jetty(6.1.26) ++---+ + + Response Body: + ++---+ + + + default + ++---+ + + HTTP Request + +----- + PUT http:///ws/v1/cluster/apps/application_1399397633663_0003/queue +---- + + Request Body: + ++---+ + + + test + ++---+ + + Response Header: + ++---+ +HTTP/1.1 200 OK +Content-Type: application/xml +Content-Length: 95 +Server: Jetty(6.1.26) ++---+ + + Response Body: + ++---+ + + + test + ++---+ + * Cluster {Delegation Tokens API} The Delegation Tokens API can be used to create, renew and cancel YARN ResourceManager delegation tokens. All delegation token requests must be carried out on a Kerberos authenticated connection(using SPNEGO). Carrying out operations on a non-kerberos connection will result in a FORBIDDEN response. In case of renewing a token, only the renewer specified when creating the token can renew the token. Other users(including the owner) are forbidden from renewing tokens. It should be noted that when cancelling or renewing a token, the token to be cancelled or renewed is specified by setting a header.