YARN-6314. Potential infinite redirection on YARN log redirection web service. Contributed by Xuan Gong.

(cherry picked from commit 5a9dda796f0e73060ada794ad5752cc6a237ab2e)
This commit is contained in:
Junping Du 2017-03-14 02:56:18 -07:00
parent 023b941e3b
commit 34424e98a6
5 changed files with 54 additions and 8 deletions

View File

@ -28,6 +28,7 @@
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.PathParam; import javax.ws.rs.PathParam;
@ -235,6 +236,8 @@ public ContainerInfo getContainer(@Context HttpServletRequest req,
* The container ID * The container ID
* @param nmId * @param nmId
* The Node Manager NodeId * The Node Manager NodeId
* @param redirected_from_node
* Whether this is a redirected request from NM
* @return * @return
* The log file's name and current file size * The log file's name and current file size
*/ */
@ -245,7 +248,9 @@ public Response getContainerLogsInfo(
@Context HttpServletRequest req, @Context HttpServletRequest req,
@Context HttpServletResponse res, @Context HttpServletResponse res,
@PathParam(YarnWebServiceParams.CONTAINER_ID) String containerIdStr, @PathParam(YarnWebServiceParams.CONTAINER_ID) String containerIdStr,
@QueryParam(YarnWebServiceParams.NM_ID) String nmId) { @QueryParam(YarnWebServiceParams.NM_ID) String nmId,
@QueryParam(YarnWebServiceParams.REDIRECTED_FROM_NODE)
@DefaultValue("false") boolean redirected_from_node) {
ContainerId containerId = null; ContainerId containerId = null;
init(res); init(res);
try { try {
@ -253,6 +258,7 @@ public Response getContainerLogsInfo(
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
throw new BadRequestException("invalid container id, " + containerIdStr); throw new BadRequestException("invalid container id, " + containerIdStr);
} }
ApplicationId appId = containerId.getApplicationAttemptId() ApplicationId appId = containerId.getApplicationAttemptId()
.getApplicationId(); .getApplicationId();
AppInfo appInfo; AppInfo appInfo;
@ -297,9 +303,12 @@ public Response getContainerLogsInfo(
// make sure nodeHttpAddress is not null and not empty. Otherwise, // make sure nodeHttpAddress is not null and not empty. Otherwise,
// we would only get log meta for aggregated logs instead of // we would only get log meta for aggregated logs instead of
// re-directing the request // re-directing the request
if (nodeHttpAddress == null || nodeHttpAddress.isEmpty()) { if (nodeHttpAddress == null || nodeHttpAddress.isEmpty()
|| redirected_from_node) {
// return log meta for the aggregated logs if exists. // return log meta for the aggregated logs if exists.
// It will also return empty log meta for the local logs. // It will also return empty log meta for the local logs.
// If this is the redirect request from NM, we should not
// re-direct the request back. Simply output the aggregated log meta.
return getContainerLogMeta(appId, appOwner, null, return getContainerLogMeta(appId, appOwner, null,
containerIdStr, true); containerIdStr, true);
} }
@ -338,6 +347,8 @@ public Response getContainerLogsInfo(
* the size of the log file * the size of the log file
* @param nmId * @param nmId
* The Node Manager NodeId * The Node Manager NodeId
* @param redirected_from_node
* Whether this is the redirect request from NM
* @return * @return
* The contents of the container's log file * The contents of the container's log file
*/ */
@ -352,9 +363,11 @@ public Response getContainerLogFile(@Context HttpServletRequest req,
@PathParam(YarnWebServiceParams.CONTAINER_LOG_FILE_NAME) String filename, @PathParam(YarnWebServiceParams.CONTAINER_LOG_FILE_NAME) String filename,
@QueryParam(YarnWebServiceParams.RESPONSE_CONTENT_FORMAT) String format, @QueryParam(YarnWebServiceParams.RESPONSE_CONTENT_FORMAT) String format,
@QueryParam(YarnWebServiceParams.RESPONSE_CONTENT_SIZE) String size, @QueryParam(YarnWebServiceParams.RESPONSE_CONTENT_SIZE) String size,
@QueryParam(YarnWebServiceParams.NM_ID) String nmId) { @QueryParam(YarnWebServiceParams.NM_ID) String nmId,
@QueryParam(YarnWebServiceParams.REDIRECTED_FROM_NODE)
boolean redirected_from_node) {
return getLogs(req, res, containerIdStr, filename, format, return getLogs(req, res, containerIdStr, filename, format,
size, nmId); size, nmId, redirected_from_node);
} }
//TODO: YARN-4993: Refactory ContainersLogsBlock, AggregatedLogsBlock and //TODO: YARN-4993: Refactory ContainersLogsBlock, AggregatedLogsBlock and
@ -371,7 +384,9 @@ public Response getLogs(@Context HttpServletRequest req,
@PathParam(YarnWebServiceParams.CONTAINER_LOG_FILE_NAME) String filename, @PathParam(YarnWebServiceParams.CONTAINER_LOG_FILE_NAME) String filename,
@QueryParam(YarnWebServiceParams.RESPONSE_CONTENT_FORMAT) String format, @QueryParam(YarnWebServiceParams.RESPONSE_CONTENT_FORMAT) String format,
@QueryParam(YarnWebServiceParams.RESPONSE_CONTENT_SIZE) String size, @QueryParam(YarnWebServiceParams.RESPONSE_CONTENT_SIZE) String size,
@QueryParam(YarnWebServiceParams.NM_ID) String nmId) { @QueryParam(YarnWebServiceParams.NM_ID) String nmId,
@QueryParam(YarnWebServiceParams.REDIRECTED_FROM_NODE)
@DefaultValue("false") boolean redirected_from_node) {
init(res); init(res);
ContainerId containerId; ContainerId containerId;
try { try {
@ -426,8 +441,11 @@ public Response getLogs(@Context HttpServletRequest req,
nodeHttpAddress = containerInfo.getNodeHttpAddress(); nodeHttpAddress = containerInfo.getNodeHttpAddress();
// make sure nodeHttpAddress is not null and not empty. Otherwise, // make sure nodeHttpAddress is not null and not empty. Otherwise,
// we would only get aggregated logs instead of re-directing the // we would only get aggregated logs instead of re-directing the
// request // request.
if (nodeHttpAddress == null || nodeHttpAddress.isEmpty()) { // If this is the redirect request from NM, we should not re-direct the
// request back. Simply output the aggregated logs.
if (nodeHttpAddress == null || nodeHttpAddress.isEmpty()
|| redirected_from_node) {
// output the aggregated logs // output the aggregated logs
return sendStreamOutputResponse(appId, appOwner, null, return sendStreamOutputResponse(appId, appOwner, null,
containerIdStr, filename, format, length, true); containerIdStr, filename, format, length, true);

View File

@ -771,6 +771,23 @@ public void testContainerLogsForRunningApps() throws Exception {
assertTrue(responseText.contains("LogAggregationType: " assertTrue(responseText.contains("LogAggregationType: "
+ ContainerLogAggregationType.LOCAL)); + ContainerLogAggregationType.LOCAL));
assertTrue(responseText.contains(AHSWebServices.getNoRedirectWarning())); assertTrue(responseText.contains(AHSWebServices.getNoRedirectWarning()));
// If this is the redirect request, we would not re-direct the request
// back and get the aggregated logs.
String content1 = "Hello." + containerId1;
NodeId nodeId1 = NodeId.fromString(NM_ID);
TestContainerLogsUtils.createContainerLogFileInRemoteFS(conf, fs,
rootLogDir, containerId1, nodeId1, fileName, user, content1, true);
response = r.path("ws").path("v1")
.path("applicationhistory").path("containers")
.path(containerId1.toString()).path("logs").path(fileName)
.queryParam("user.name", user)
.queryParam(YarnWebServiceParams.REDIRECTED_FROM_NODE, "true")
.accept(MediaType.TEXT_PLAIN).get(ClientResponse.class);
responseText = response.getEntity(String.class);
assertTrue(responseText.contains(content1));
assertTrue(responseText.contains("LogAggregationType: "
+ ContainerLogAggregationType.AGGREGATED));
} }
@Test(timeout = 10000) @Test(timeout = 10000)

View File

@ -34,4 +34,5 @@ public interface YarnWebServiceParams {
String RESPONSE_CONTENT_FORMAT = "format"; String RESPONSE_CONTENT_FORMAT = "format";
String RESPONSE_CONTENT_SIZE = "size"; String RESPONSE_CONTENT_SIZE = "size";
String NM_ID = "nm.id"; String NM_ID = "nm.id";
String REDIRECTED_FROM_NODE = "redirected_from_node";
} }

View File

@ -499,7 +499,11 @@ private Response createRedirectResponse(HttpServletRequest httpRequest,
String requestParams = WebAppUtils.removeQueryParams(httpRequest, String requestParams = WebAppUtils.removeQueryParams(httpRequest,
YarnWebServiceParams.NM_ID); YarnWebServiceParams.NM_ID);
if (requestParams != null && !requestParams.isEmpty()) { if (requestParams != null && !requestParams.isEmpty()) {
redirectPath.append("?" + requestParams); redirectPath.append("?" + requestParams + "&"
+ YarnWebServiceParams.REDIRECTED_FROM_NODE + "=true");
} else {
redirectPath.append("?" + YarnWebServiceParams.REDIRECTED_FROM_NODE
+ "=true");
} }
ResponseBuilder res = Response.status( ResponseBuilder res = Response.status(
HttpServletResponse.SC_TEMPORARY_REDIRECT); HttpServletResponse.SC_TEMPORARY_REDIRECT);

View File

@ -383,6 +383,8 @@ public void testNMRedirect() {
assertTrue(redirectURL.contains(noExistContainerId.toString())); assertTrue(redirectURL.contains(noExistContainerId.toString()));
assertTrue(redirectURL.contains("/logs/" + fileName)); assertTrue(redirectURL.contains("/logs/" + fileName));
assertTrue(redirectURL.contains("user.name=" + "user")); assertTrue(redirectURL.contains("user.name=" + "user"));
assertTrue(redirectURL.contains(
YarnWebServiceParams.REDIRECTED_FROM_NODE + "=true"));
assertFalse(redirectURL.contains(YarnWebServiceParams.NM_ID)); assertFalse(redirectURL.contains(YarnWebServiceParams.NM_ID));
// check the new api // check the new api
@ -397,6 +399,8 @@ public void testNMRedirect() {
assertTrue(redirectURL.contains(noExistContainerId.toString())); assertTrue(redirectURL.contains(noExistContainerId.toString()));
assertTrue(redirectURL.contains("/logs/" + fileName)); assertTrue(redirectURL.contains("/logs/" + fileName));
assertTrue(redirectURL.contains("user.name=" + "user")); assertTrue(redirectURL.contains("user.name=" + "user"));
assertTrue(redirectURL.contains(
YarnWebServiceParams.REDIRECTED_FROM_NODE + "=true"));
assertFalse(redirectURL.contains(YarnWebServiceParams.NM_ID)); assertFalse(redirectURL.contains(YarnWebServiceParams.NM_ID));
requestURI = r.path("ws").path("v1").path("node") requestURI = r.path("ws").path("v1").path("node")
@ -409,6 +413,8 @@ public void testNMRedirect() {
assertTrue(redirectURL.contains(LOGSERVICEWSADDR)); assertTrue(redirectURL.contains(LOGSERVICEWSADDR));
assertTrue(redirectURL.contains(noExistContainerId.toString())); assertTrue(redirectURL.contains(noExistContainerId.toString()));
assertTrue(redirectURL.contains("user.name=" + "user")); assertTrue(redirectURL.contains("user.name=" + "user"));
assertTrue(redirectURL.contains(
YarnWebServiceParams.REDIRECTED_FROM_NODE + "=true"));
assertFalse(redirectURL.contains(YarnWebServiceParams.NM_ID)); assertFalse(redirectURL.contains(YarnWebServiceParams.NM_ID));
} }