YARN-5649. Add REST endpoints for updating application timeouts. Contributed by Rohith Sharma K S

This commit is contained in:
Jian He 2016-11-23 16:25:29 -08:00
parent 3d94da1e00
commit 1f12867a69
11 changed files with 472 additions and 6 deletions

View File

@ -1752,7 +1752,7 @@ public class ClientRMService extends AbstractService implements
RMAuditLogger.logFailure(callerUGI.getShortUserName(), RMAuditLogger.logFailure(callerUGI.getShortUserName(),
AuditConstants.UPDATE_APP_TIMEOUTS, "UNKNOWN", "ClientRMService", AuditConstants.UPDATE_APP_TIMEOUTS, "UNKNOWN", "ClientRMService",
ex.getMessage()); ex.getMessage());
throw RPCUtil.getRemoteException(ex); throw ex;
} }
RMAuditLogger.logSuccess(callerUGI.getShortUserName(), RMAuditLogger.logSuccess(callerUGI.getShortUserName(),

View File

@ -66,6 +66,7 @@ public class RMAuditLogger {
"Update Application Priority"; "Update Application Priority";
public static final String UPDATE_APP_TIMEOUTS = public static final String UPDATE_APP_TIMEOUTS =
"Update Application Timeouts"; "Update Application Timeouts";
public static final String GET_APP_TIMEOUTS = "Get Application Timeouts";
public static final String CHANGE_CONTAINER_RESOURCE = public static final String CHANGE_CONTAINER_RESOURCE =
"AM Changed Container Resource"; "AM Changed Container Resource";
public static final String SIGNAL_CONTAINER = "Signal Container Request"; public static final String SIGNAL_CONTAINER = "Signal Container Request";

View File

@ -515,7 +515,7 @@ public class RMServerUtils {
String message = String message =
"Expire time is not in ISO8601 format. ISO8601 supported " "Expire time is not in ISO8601 format. ISO8601 supported "
+ "format is yyyy-MM-dd'T'HH:mm:ss.SSSZ"; + "format is yyyy-MM-dd'T'HH:mm:ss.SSSZ";
throw new YarnException(message); throw new YarnException(message, ex);
} }
if (expireTime < currentTimeMillis) { if (expireTime < currentTimeMillis) {
String message = String message =

View File

@ -54,7 +54,8 @@ public class JAXBContextResolver implements ContextResolver<JAXBContext> {
CapacitySchedulerQueueInfoList.class, ResourceInfo.class, CapacitySchedulerQueueInfoList.class, ResourceInfo.class,
UsersInfo.class, UserInfo.class, ApplicationStatisticsInfo.class, UsersInfo.class, UserInfo.class, ApplicationStatisticsInfo.class,
StatisticsItemInfo.class, CapacitySchedulerHealthInfo.class, StatisticsItemInfo.class, CapacitySchedulerHealthInfo.class,
FairSchedulerQueueInfoList.class}; FairSchedulerQueueInfoList.class, AppTimeoutsInfo.class,
AppTimeoutInfo.class };
// these dao classes need root unwrapping // these dao classes need root unwrapping
final Class[] rootUnwrappedTypes = final Class[] rootUnwrappedTypes =
{ NewApplication.class, ApplicationSubmissionContextInfo.class, { NewApplication.class, ApplicationSubmissionContextInfo.class,

View File

@ -24,9 +24,11 @@ import java.nio.ByteBuffer;
import java.security.AccessControlException; import java.security.AccessControlException;
import java.security.Principal; import java.security.Principal;
import java.security.PrivilegedExceptionAction; import java.security.PrivilegedExceptionAction;
import java.text.ParseException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -96,10 +98,12 @@ import org.apache.hadoop.yarn.api.protocolrecords.ReservationUpdateRequest;
import org.apache.hadoop.yarn.api.protocolrecords.SubmitApplicationRequest; import org.apache.hadoop.yarn.api.protocolrecords.SubmitApplicationRequest;
import org.apache.hadoop.yarn.api.protocolrecords.SubmitApplicationResponse; import org.apache.hadoop.yarn.api.protocolrecords.SubmitApplicationResponse;
import org.apache.hadoop.yarn.api.protocolrecords.UpdateApplicationPriorityRequest; import org.apache.hadoop.yarn.api.protocolrecords.UpdateApplicationPriorityRequest;
import org.apache.hadoop.yarn.api.protocolrecords.UpdateApplicationTimeoutsRequest;
import org.apache.hadoop.yarn.api.records.ApplicationAccessType; import org.apache.hadoop.yarn.api.records.ApplicationAccessType;
import org.apache.hadoop.yarn.api.records.ApplicationId; import org.apache.hadoop.yarn.api.records.ApplicationId;
import org.apache.hadoop.yarn.api.records.ApplicationReport; import org.apache.hadoop.yarn.api.records.ApplicationReport;
import org.apache.hadoop.yarn.api.records.ApplicationSubmissionContext; import org.apache.hadoop.yarn.api.records.ApplicationSubmissionContext;
import org.apache.hadoop.yarn.api.records.ApplicationTimeoutType;
import org.apache.hadoop.yarn.api.records.ContainerLaunchContext; import org.apache.hadoop.yarn.api.records.ContainerLaunchContext;
import org.apache.hadoop.yarn.api.records.FinalApplicationStatus; import org.apache.hadoop.yarn.api.records.FinalApplicationStatus;
import org.apache.hadoop.yarn.api.records.LocalResource; import org.apache.hadoop.yarn.api.records.LocalResource;
@ -189,6 +193,7 @@ import org.apache.hadoop.yarn.server.webapp.dao.ContainerInfo;
import org.apache.hadoop.yarn.server.webapp.dao.ContainersInfo; import org.apache.hadoop.yarn.server.webapp.dao.ContainersInfo;
import org.apache.hadoop.yarn.util.AdHocLogDumper; import org.apache.hadoop.yarn.util.AdHocLogDumper;
import org.apache.hadoop.yarn.util.ConverterUtils; import org.apache.hadoop.yarn.util.ConverterUtils;
import org.apache.hadoop.yarn.util.Times;
import org.apache.hadoop.yarn.webapp.BadRequestException; import org.apache.hadoop.yarn.webapp.BadRequestException;
import org.apache.hadoop.yarn.webapp.ForbiddenException; import org.apache.hadoop.yarn.webapp.ForbiddenException;
import org.apache.hadoop.yarn.webapp.NotFoundException; import org.apache.hadoop.yarn.webapp.NotFoundException;
@ -2428,4 +2433,185 @@ public class RMWebServices extends WebServices {
return Response.status(Status.OK).entity(resResponse).build(); return Response.status(Status.OK).entity(resResponse).build();
} }
@GET
@Path("/apps/{appid}/timeout/{type}")
@Produces({ MediaType.APPLICATION_JSON + "; " + JettyUtils.UTF_8,
MediaType.APPLICATION_XML + "; " + JettyUtils.UTF_8 })
public AppTimeoutInfo getAppTimeout(@Context HttpServletRequest hsr,
@PathParam("appid") String appId, @PathParam("type") String type)
throws AuthorizationException {
init();
RMApp app = validateAppTimeoutRequest(hsr, appId);
ApplicationTimeoutType appTimeoutType = parseTimeoutType(type);
Long timeoutValue = app.getApplicationTimeouts().get(appTimeoutType);
AppTimeoutInfo timeout =
constructAppTimeoutDao(appTimeoutType, timeoutValue);
return timeout;
}
private RMApp validateAppTimeoutRequest(HttpServletRequest hsr,
String appId) {
UserGroupInformation callerUGI = getCallerUserGroupInformation(hsr, true);
String userName = "UNKNOWN-USER";
if (callerUGI != null) {
userName = callerUGI.getUserName();
}
if (UserGroupInformation.isSecurityEnabled() && isStaticUser(callerUGI)) {
String msg = "The default static user cannot carry out this operation.";
RMAuditLogger.logFailure(userName, AuditConstants.GET_APP_TIMEOUTS,
"UNKNOWN", "RMWebService", msg);
throw new ForbiddenException(msg);
}
RMApp app = null;
try {
app = getRMAppForAppId(appId);
} catch (NotFoundException e) {
RMAuditLogger.logFailure(userName, AuditConstants.GET_APP_TIMEOUTS,
"UNKNOWN", "RMWebService",
"Trying to get timeouts of an absent application " + appId);
throw e;
}
return app;
}
@GET
@Path("/apps/{appid}/timeouts")
@Produces({ MediaType.APPLICATION_JSON + "; " + JettyUtils.UTF_8,
MediaType.APPLICATION_XML + "; " + JettyUtils.UTF_8 })
public AppTimeoutsInfo getAppTimeouts(@Context HttpServletRequest hsr,
@PathParam("appid") String appId) throws AuthorizationException {
init();
RMApp app = validateAppTimeoutRequest(hsr, appId);
AppTimeoutsInfo timeouts = new AppTimeoutsInfo();
Map<ApplicationTimeoutType, Long> applicationTimeouts =
app.getApplicationTimeouts();
if (applicationTimeouts.isEmpty()) {
// If application is not set timeout, lifetime should be sent as default
// with expiryTime=UNLIMITED and remainingTime=-1
timeouts
.add(constructAppTimeoutDao(ApplicationTimeoutType.LIFETIME, null));
} else {
for (Entry<ApplicationTimeoutType, Long> timeout : app
.getApplicationTimeouts().entrySet()) {
AppTimeoutInfo timeoutInfo =
constructAppTimeoutDao(timeout.getKey(), timeout.getValue());
timeouts.add(timeoutInfo);
}
}
return timeouts;
}
private ApplicationTimeoutType parseTimeoutType(String type) {
try {
// enum string is in the uppercase
return ApplicationTimeoutType
.valueOf(StringUtils.toUpperCase(type.trim()));
} catch (RuntimeException e) {
ApplicationTimeoutType[] typeArray = ApplicationTimeoutType.values();
String allAppTimeoutTypes = Arrays.toString(typeArray);
throw new BadRequestException("Invalid application-state " + type.trim()
+ " specified. It should be one of " + allAppTimeoutTypes);
}
}
private AppTimeoutInfo constructAppTimeoutDao(ApplicationTimeoutType type,
Long timeoutInMillis) {
AppTimeoutInfo timeout = new AppTimeoutInfo();
timeout.setTimeoutType(type);
if (timeoutInMillis != null) {
timeout.setExpiryTime(Times.formatISO8601(timeoutInMillis.longValue()));
timeout.setRemainingTime(
Math.max((timeoutInMillis - System.currentTimeMillis()) / 1000, 0));
}
return timeout;
}
@PUT
@Path("/apps/{appid}/timeout")
@Produces({ MediaType.APPLICATION_JSON + "; " + JettyUtils.UTF_8,
MediaType.APPLICATION_XML + "; " + JettyUtils.UTF_8 })
@Consumes({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public Response updateApplicationTimeout(AppTimeoutInfo appTimeout,
@Context HttpServletRequest hsr, @PathParam("appid") String appId)
throws AuthorizationException, YarnException, InterruptedException,
IOException {
init();
UserGroupInformation callerUGI = getCallerUserGroupInformation(hsr, true);
if (callerUGI == null) {
throw new AuthorizationException(
"Unable to obtain user name, user not authenticated");
}
if (UserGroupInformation.isSecurityEnabled() && isStaticUser(callerUGI)) {
return Response.status(Status.FORBIDDEN)
.entity("The default static user cannot carry out this operation.")
.build();
}
String userName = callerUGI.getUserName();
RMApp app = null;
try {
app = getRMAppForAppId(appId);
} catch (NotFoundException e) {
RMAuditLogger.logFailure(userName, AuditConstants.UPDATE_APP_TIMEOUTS,
"UNKNOWN", "RMWebService",
"Trying to update timeout of an absent application " + appId);
throw e;
}
return updateApplicationTimeouts(app, callerUGI, appTimeout);
}
private Response updateApplicationTimeouts(final RMApp app,
UserGroupInformation callerUGI, final AppTimeoutInfo appTimeout)
throws IOException, InterruptedException {
if (appTimeout.getTimeoutType() == null) {
return Response.status(Status.BAD_REQUEST).entity("Timeout type is null.")
.build();
}
String userName = callerUGI.getUserName();
try {
callerUGI.doAs(new PrivilegedExceptionAction<Void>() {
@Override
public Void run() throws IOException, YarnException {
UpdateApplicationTimeoutsRequest request =
UpdateApplicationTimeoutsRequest
.newInstance(app.getApplicationId(), Collections.singletonMap(
appTimeout.getTimeoutType(), appTimeout.getExpireTime()));
rm.getClientRMService().updateApplicationTimeouts(request);
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 change timeout of app " + appId
+ " by remote user " + userName;
return Response.status(Status.FORBIDDEN).entity(msg).build();
} else if (ye.getCause() instanceof ParseException) {
return Response.status(Status.BAD_REQUEST)
.entity(ye.getMessage()).build();
} else {
throw ue;
}
} else {
throw ue;
}
}
AppTimeoutInfo timeout = constructAppTimeoutDao(appTimeout.getTimeoutType(),
app.getApplicationTimeouts().get(appTimeout.getTimeoutType()));
return Response.status(Status.OK).entity(timeout).build();
}
} }

View File

@ -19,6 +19,7 @@ package org.apache.hadoop.yarn.server.resourcemanager.webapp.dao;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAccessorType;
@ -29,6 +30,7 @@ import javax.xml.bind.annotation.XmlTransient;
import org.apache.hadoop.yarn.api.records.ApplicationId; import org.apache.hadoop.yarn.api.records.ApplicationId;
import org.apache.hadoop.yarn.api.records.ApplicationResourceUsageReport; import org.apache.hadoop.yarn.api.records.ApplicationResourceUsageReport;
import org.apache.hadoop.yarn.api.records.ApplicationSubmissionContext; import org.apache.hadoop.yarn.api.records.ApplicationSubmissionContext;
import org.apache.hadoop.yarn.api.records.ApplicationTimeoutType;
import org.apache.hadoop.yarn.api.records.Container; import org.apache.hadoop.yarn.api.records.Container;
import org.apache.hadoop.yarn.api.records.FinalApplicationStatus; import org.apache.hadoop.yarn.api.records.FinalApplicationStatus;
import org.apache.hadoop.yarn.api.records.LogAggregationStatus; import org.apache.hadoop.yarn.api.records.LogAggregationStatus;
@ -114,6 +116,7 @@ public class AppInfo {
protected String amNodeLabelExpression; protected String amNodeLabelExpression;
protected ResourcesInfo resourceInfo = null; protected ResourcesInfo resourceInfo = null;
protected AppTimeoutsInfo timeouts = new AppTimeoutsInfo();
public AppInfo() { public AppInfo() {
} // JAXB needs this } // JAXB needs this
@ -240,6 +243,27 @@ public class AppInfo {
: null; : null;
} }
} }
Map<ApplicationTimeoutType, Long> applicationTimeouts =
app.getApplicationTimeouts();
if (applicationTimeouts.isEmpty()) {
// If application is not set timeout, lifetime should be sent as default
// with expiryTime=UNLIMITED and remainingTime=-1
AppTimeoutInfo timeoutInfo = new AppTimeoutInfo();
timeoutInfo.setTimeoutType(ApplicationTimeoutType.LIFETIME);
timeouts.add(timeoutInfo);
} else {
for (Map.Entry<ApplicationTimeoutType, Long> entry : app
.getApplicationTimeouts().entrySet()) {
AppTimeoutInfo timeout = new AppTimeoutInfo();
timeout.setTimeoutType(entry.getKey());
long timeoutInMillis = entry.getValue().longValue();
timeout.setExpiryTime(Times.formatISO8601(timeoutInMillis));
timeout.setRemainingTime(Math
.max((timeoutInMillis - System.currentTimeMillis()) / 1000, 0));
timeouts.add(timeout);
}
}
} }
} }

View File

@ -0,0 +1,71 @@
/**
* 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.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import org.apache.hadoop.yarn.api.records.ApplicationTimeoutType;
/**
* DAO object to display Application timeout information.
*/
@XmlRootElement(name = "timeout")
@XmlAccessorType(XmlAccessType.FIELD)
public class AppTimeoutInfo {
@XmlElement(name = "type")
private ApplicationTimeoutType timeoutType;
@XmlElement(name = "expiryTime")
private String expiryTime;
@XmlElement(name = "remainingTimeInSeconds")
private long remainingTimeInSec;
public AppTimeoutInfo() {
expiryTime = "UNLIMITED";
remainingTimeInSec = -1;
}
public ApplicationTimeoutType getTimeoutType() {
return timeoutType;
}
public String getExpireTime() {
return expiryTime;
}
public long getRemainingTimeInSec() {
return remainingTimeInSec;
}
public void setTimeoutType(ApplicationTimeoutType type) {
this.timeoutType = type;
}
public void setExpiryTime(String expiryTime) {
this.expiryTime = expiryTime;
}
public void setRemainingTime(long remainingTime) {
this.remainingTimeInSec = remainingTime;
}
}

View File

@ -0,0 +1,47 @@
/**
* 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 java.util.ArrayList;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
/**
* This class hosts a set of AppTimeout DAO objects.
*/
@XmlRootElement(name = "timeouts")
@XmlAccessorType(XmlAccessType.FIELD)
public class AppTimeoutsInfo {
@XmlElement(name = "timeout")
private ArrayList<AppTimeoutInfo> timeouts = new ArrayList<AppTimeoutInfo>();
public AppTimeoutsInfo() {
} // JAXB needs this
public void add(AppTimeoutInfo timeoutInfo) {
timeouts.add(timeoutInfo);
}
public ArrayList<AppTimeoutInfo> getAppTimeouts() {
return timeouts;
}
}

View File

@ -19,6 +19,7 @@
package org.apache.hadoop.yarn.server.resourcemanager.rmapp; package org.apache.hadoop.yarn.server.resourcemanager.rmapp;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -319,7 +320,7 @@ public class MockRMApp implements RMApp {
@Override @Override
public Map<ApplicationTimeoutType, Long> getApplicationTimeouts() { public Map<ApplicationTimeoutType, Long> getApplicationTimeouts() {
throw new UnsupportedOperationException("Not supported yet."); return Collections.emptyMap();
} }
@Override @Override

View File

@ -1419,7 +1419,7 @@ public class TestRMWebServicesApps extends JerseyTestBase {
public void verifyAppInfo(JSONObject info, RMApp app) throws JSONException, public void verifyAppInfo(JSONObject info, RMApp app) throws JSONException,
Exception { Exception {
int expectedNumberOfElements = 34; int expectedNumberOfElements = 35;
String appNodeLabelExpression = null; String appNodeLabelExpression = null;
String amNodeLabelExpression = null; String amNodeLabelExpression = null;
if (app.getApplicationSubmissionContext() if (app.getApplicationSubmissionContext()

View File

@ -61,6 +61,7 @@ import org.apache.hadoop.security.authentication.server.PseudoAuthenticationHand
import org.apache.hadoop.yarn.api.records.ApplicationAccessType; import org.apache.hadoop.yarn.api.records.ApplicationAccessType;
import org.apache.hadoop.yarn.api.records.ApplicationId; import org.apache.hadoop.yarn.api.records.ApplicationId;
import org.apache.hadoop.yarn.api.records.ApplicationSubmissionContext; import org.apache.hadoop.yarn.api.records.ApplicationSubmissionContext;
import org.apache.hadoop.yarn.api.records.ApplicationTimeoutType;
import org.apache.hadoop.yarn.api.records.ContainerLaunchContext; import org.apache.hadoop.yarn.api.records.ContainerLaunchContext;
import org.apache.hadoop.yarn.api.records.LocalResource; import org.apache.hadoop.yarn.api.records.LocalResource;
import org.apache.hadoop.yarn.api.records.LocalResourceType; import org.apache.hadoop.yarn.api.records.LocalResourceType;
@ -83,15 +84,17 @@ import org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairSchedule
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.AppPriority; import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.AppPriority;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.AppQueue; import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.AppQueue;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.AppState; import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.AppState;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.AppTimeoutInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.ApplicationSubmissionContextInfo; 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.CredentialsInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.LocalResourceInfo; import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.LocalResourceInfo;
import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.LogAggregationContextInfo; import org.apache.hadoop.yarn.server.resourcemanager.webapp.dao.LogAggregationContextInfo;
import org.apache.hadoop.yarn.util.ConverterUtils; import org.apache.hadoop.yarn.util.Times;
import org.apache.hadoop.yarn.webapp.GenericExceptionHandler; import org.apache.hadoop.yarn.webapp.GenericExceptionHandler;
import org.apache.hadoop.yarn.webapp.GuiceServletConfig; import org.apache.hadoop.yarn.webapp.GuiceServletConfig;
import org.apache.hadoop.yarn.webapp.JerseyTestBase; import org.apache.hadoop.yarn.webapp.JerseyTestBase;
import org.apache.hadoop.yarn.webapp.WebServicesTestUtils; import org.apache.hadoop.yarn.webapp.WebServicesTestUtils;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException; import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject; import org.codehaus.jettison.json.JSONObject;
import org.junit.After; import org.junit.After;
@ -117,6 +120,7 @@ import com.sun.jersey.api.client.ClientResponse.Status;
import com.sun.jersey.api.client.WebResource; import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.DefaultClientConfig; import com.sun.jersey.api.client.config.DefaultClientConfig;
import com.sun.jersey.api.client.filter.LoggingFilter; import com.sun.jersey.api.client.filter.LoggingFilter;
import com.sun.jersey.api.json.JSONConfiguration;
import com.sun.jersey.api.json.JSONJAXBContext; import com.sun.jersey.api.json.JSONJAXBContext;
import com.sun.jersey.api.json.JSONMarshaller; import com.sun.jersey.api.json.JSONMarshaller;
import com.sun.jersey.guice.spi.container.servlet.GuiceContainer; import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;
@ -1295,4 +1299,135 @@ public class TestRMWebServicesAppsModification extends JerseyTestBase {
assertEquals(queue, responseQueue); assertEquals(queue, responseQueue);
} }
@Test(timeout = 90000)
public void testUpdateAppTimeout() throws Exception {
client().addFilter(new LoggingFilter(System.out));
rm.start();
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) {
// application submitted without timeout
RMApp app = rm.submitApp(CONTAINER_MB, "", webserviceUserName);
ClientResponse response =
this.constructWebResource("apps", app.getApplicationId().toString(),
"timeouts").accept(mediaType).get(ClientResponse.class);
if (mediaType.contains(MediaType.APPLICATION_JSON)) {
assertEquals(
MediaType.APPLICATION_JSON_TYPE + "; " + JettyUtils.UTF_8,
response.getType().toString());
JSONObject js =
response.getEntity(JSONObject.class).getJSONObject("timeouts");
JSONArray entity = js.getJSONArray("timeout");
verifyAppTimeoutJson(entity.getJSONObject(0),
ApplicationTimeoutType.LIFETIME, "UNLIMITED", -1);
}
AppTimeoutInfo timeoutUpdate = new AppTimeoutInfo();
long timeOutFromNow = 60;
String expireTime = Times
.formatISO8601(System.currentTimeMillis() + timeOutFromNow * 1000);
timeoutUpdate.setTimeoutType(ApplicationTimeoutType.LIFETIME);
timeoutUpdate.setExpiryTime(expireTime);
Object entity;
if (contentType.equals(MediaType.APPLICATION_JSON_TYPE)) {
entity = appTimeoutToJSON(timeoutUpdate);
} else {
entity = timeoutUpdate;
}
response = this
.constructWebResource("apps", app.getApplicationId().toString(),
"timeout")
.entity(entity, contentType).accept(mediaType)
.put(ClientResponse.class);
if (!isAuthenticationEnabled()) {
assertResponseStatusCode(Status.UNAUTHORIZED,
response.getStatusInfo());
continue;
}
assertResponseStatusCode(Status.OK, response.getStatusInfo());
if (mediaType.contains(MediaType.APPLICATION_JSON)) {
verifyAppTimeoutJson(response, ApplicationTimeoutType.LIFETIME,
expireTime, timeOutFromNow);
} else {
verifyAppTimeoutXML(response, ApplicationTimeoutType.LIFETIME,
expireTime, timeOutFromNow);
}
// invoke get
response =
this.constructWebResource("apps", app.getApplicationId().toString(),
"timeout", ApplicationTimeoutType.LIFETIME.toString())
.accept(mediaType).get(ClientResponse.class);
assertResponseStatusCode(Status.OK, response.getStatusInfo());
if (mediaType.contains(MediaType.APPLICATION_JSON)) {
verifyAppTimeoutJson(response, ApplicationTimeoutType.LIFETIME,
expireTime, timeOutFromNow);
}
}
}
rm.stop();
}
protected static void verifyAppTimeoutJson(ClientResponse response,
ApplicationTimeoutType type, String expireTime, long timeOutFromNow)
throws JSONException {
assertEquals(MediaType.APPLICATION_JSON_TYPE + "; " + JettyUtils.UTF_8,
response.getType().toString());
JSONObject jsonTimeout = response.getEntity(JSONObject.class);
assertEquals("incorrect number of elements", 1, jsonTimeout.length());
JSONObject json = jsonTimeout.getJSONObject("timeout");
verifyAppTimeoutJson(json, type, expireTime, timeOutFromNow);
}
protected static void verifyAppTimeoutJson(JSONObject json,
ApplicationTimeoutType type, String expireTime, long timeOutFromNow)
throws JSONException {
assertEquals("incorrect number of elements", 3, json.length());
assertEquals(type.toString(), json.getString("type"));
assertEquals(expireTime, json.getString("expiryTime"));
assertTrue(json.getLong("remainingTimeInSeconds") <= timeOutFromNow);
}
protected static void verifyAppTimeoutXML(ClientResponse response,
ApplicationTimeoutType type, String expireTime, long timeOutFromNow)
throws ParserConfigurationException, IOException, SAXException {
assertEquals(MediaType.APPLICATION_XML_TYPE + "; " + JettyUtils.UTF_8,
response.getType().toString());
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("timeout");
assertEquals("incorrect number of elements", 1, nodes.getLength());
Element element = (Element) nodes.item(0);
assertEquals(type.toString(),
WebServicesTestUtils.getXmlString(element, "type"));
assertEquals(expireTime,
WebServicesTestUtils.getXmlString(element, "expiryTime"));
assertTrue(WebServicesTestUtils.getXmlLong(element,
"remainingTimeInSeconds") < timeOutFromNow);
}
protected static String appTimeoutToJSON(AppTimeoutInfo timeout)
throws Exception {
StringWriter sw = new StringWriter();
JSONJAXBContext ctx = new JSONJAXBContext(
JSONConfiguration.natural().rootUnwrapping(false).build(),
AppTimeoutInfo.class);
JSONMarshaller jm = ctx.createJSONMarshaller();
jm.marshallToJSON(timeout, sw);
jm.marshallToJSON(timeout, System.out);
return sw.toString();
}
} }