YARN-4990. Re-direction of a particular log file within in a container in NM UI does not redirect properly. Contributed by Xuan Gong.
(cherry picked from commit 736f54b727
)
This commit is contained in:
parent
2adce1b361
commit
689d19575f
|
@ -79,6 +79,10 @@ public class NMWebAppFilter extends GuiceContainer{
|
||||||
String[] parts = uri.split("/");
|
String[] parts = uri.split("/");
|
||||||
String containerIdStr = parts[3];
|
String containerIdStr = parts[3];
|
||||||
String appOwner = parts[4];
|
String appOwner = parts[4];
|
||||||
|
String logType = null;
|
||||||
|
if (parts.length > 5) {
|
||||||
|
logType = parts[5];
|
||||||
|
}
|
||||||
if (containerIdStr != null && !containerIdStr.isEmpty()) {
|
if (containerIdStr != null && !containerIdStr.isEmpty()) {
|
||||||
ContainerId containerId = null;
|
ContainerId containerId = null;
|
||||||
try {
|
try {
|
||||||
|
@ -106,6 +110,10 @@ public class NMWebAppFilter extends GuiceContainer{
|
||||||
sb.append(containerIdStr);
|
sb.append(containerIdStr);
|
||||||
sb.append("/");
|
sb.append("/");
|
||||||
sb.append(appOwner);
|
sb.append(appOwner);
|
||||||
|
if (logType != null && !logType.isEmpty()) {
|
||||||
|
sb.append("/");
|
||||||
|
sb.append(logType);
|
||||||
|
}
|
||||||
redirectPath =
|
redirectPath =
|
||||||
WebAppUtils.appendQueryParams(request, sb.toString());
|
WebAppUtils.appendQueryParams(request, sb.toString());
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -0,0 +1,176 @@
|
||||||
|
/**
|
||||||
|
* 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.nodemanager.webapp;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import com.google.inject.Injector;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.io.StringWriter;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import javax.servlet.FilterChain;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.ServletRequest;
|
||||||
|
import javax.servlet.ServletResponse;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import org.apache.hadoop.conf.Configuration;
|
||||||
|
import org.apache.hadoop.yarn.api.records.ApplicationAttemptId;
|
||||||
|
import org.apache.hadoop.yarn.api.records.ApplicationId;
|
||||||
|
import org.apache.hadoop.yarn.api.records.ContainerId;
|
||||||
|
import org.apache.hadoop.yarn.api.records.NodeId;
|
||||||
|
import org.apache.hadoop.yarn.conf.YarnConfiguration;
|
||||||
|
import org.apache.hadoop.yarn.server.nodemanager.LocalDirsHandlerService;
|
||||||
|
import org.apache.hadoop.yarn.server.nodemanager.NodeManager.NMContext;
|
||||||
|
import org.apache.hadoop.yarn.server.nodemanager.containermanager.application.Application;
|
||||||
|
import org.glassfish.grizzly.servlet.HttpServletResponseImpl;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Basic sanity Tests for NMWebFilter.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class TestNMWebFilter {
|
||||||
|
|
||||||
|
private static final String LOG_SERVER_URI = "log-server:1999/logs";
|
||||||
|
private static final String USER = "testUser";
|
||||||
|
|
||||||
|
@Test(timeout = 5000)
|
||||||
|
public void testRedirection() throws Exception {
|
||||||
|
ApplicationId appId = ApplicationId.newInstance(
|
||||||
|
System.currentTimeMillis(), 1);
|
||||||
|
ApplicationAttemptId attemptId = ApplicationAttemptId.newInstance(
|
||||||
|
appId, 1);
|
||||||
|
ContainerId containerId = ContainerId.newContainerId(attemptId, 1);
|
||||||
|
|
||||||
|
NMContext mockNMContext = mock(NMContext.class);
|
||||||
|
ConcurrentMap<ApplicationId, Application> applications
|
||||||
|
= new ConcurrentHashMap<>();
|
||||||
|
when(mockNMContext.getApplications()).thenReturn(applications);
|
||||||
|
LocalDirsHandlerService mockLocalDirsHandlerService = mock(
|
||||||
|
LocalDirsHandlerService.class);
|
||||||
|
Configuration conf = new Configuration();
|
||||||
|
conf.setBoolean(YarnConfiguration.LOG_AGGREGATION_ENABLED, true);
|
||||||
|
conf.set(YarnConfiguration.YARN_LOG_SERVER_URL,
|
||||||
|
"http://" + LOG_SERVER_URI);
|
||||||
|
when(mockLocalDirsHandlerService.getConfig()).thenReturn(conf);
|
||||||
|
when(mockNMContext.getLocalDirsHandler()).thenReturn(
|
||||||
|
mockLocalDirsHandlerService);
|
||||||
|
NodeId nodeId = NodeId.newInstance("testNM", 9999);
|
||||||
|
when(mockNMContext.getNodeId()).thenReturn(nodeId);
|
||||||
|
|
||||||
|
Injector mockInjector = mock(Injector.class);
|
||||||
|
NMWebAppFilter testFilter = new NMWebAppFilter(
|
||||||
|
mockInjector, mockNMContext);
|
||||||
|
|
||||||
|
HttpServletResponseForTest response = new HttpServletResponseForTest();
|
||||||
|
// dummy filter
|
||||||
|
FilterChain chain = new FilterChain() {
|
||||||
|
@Override
|
||||||
|
public void doFilter(ServletRequest servletRequest,
|
||||||
|
ServletResponse servletResponse) throws IOException,
|
||||||
|
ServletException {
|
||||||
|
// Do Nothing
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
String uri = "testNM:8042/node/containerlogs/"
|
||||||
|
+ containerId.toString() + "/" + USER;
|
||||||
|
HttpServletRequest request = mock(HttpServletRequest.class);
|
||||||
|
when(request.getRequestURI()).thenReturn(uri);
|
||||||
|
testFilter.doFilter(request, response, chain);
|
||||||
|
assertEquals(HttpServletResponse.SC_TEMPORARY_REDIRECT, response.status);
|
||||||
|
String redirect = response.getHeader("Location");
|
||||||
|
assertTrue(redirect.contains(LOG_SERVER_URI));
|
||||||
|
assertTrue(redirect.contains(nodeId.toString()));
|
||||||
|
assertTrue(redirect.contains(containerId.toString()));
|
||||||
|
assertTrue(redirect.contains(USER));
|
||||||
|
|
||||||
|
String logType = "syslog";
|
||||||
|
uri = "testNM:8042/node/containerlogs/" + containerId.toString()
|
||||||
|
+ "/" + USER + "/" + logType + "/?start=10";
|
||||||
|
HttpServletRequest request2 = mock(HttpServletRequest.class);
|
||||||
|
when(request2.getRequestURI()).thenReturn(uri);
|
||||||
|
when(request2.getQueryString()).thenReturn("start=10");
|
||||||
|
testFilter.doFilter(request2, response, chain);
|
||||||
|
assertEquals(HttpServletResponse.SC_TEMPORARY_REDIRECT, response.status);
|
||||||
|
redirect = response.getHeader("Location");
|
||||||
|
assertTrue(redirect.contains(LOG_SERVER_URI));
|
||||||
|
assertTrue(redirect.contains(nodeId.toString()));
|
||||||
|
assertTrue(redirect.contains(containerId.toString()));
|
||||||
|
assertTrue(redirect.contains(USER));
|
||||||
|
assertTrue(redirect.contains(logType));
|
||||||
|
assertTrue(redirect.contains("start=10"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private class HttpServletResponseForTest extends HttpServletResponseImpl {
|
||||||
|
String redirectLocation = "";
|
||||||
|
int status;
|
||||||
|
private String contentType;
|
||||||
|
private final Map<String, String> headers = new HashMap<>(1);
|
||||||
|
private StringWriter body;
|
||||||
|
|
||||||
|
public String getRedirect() {
|
||||||
|
return redirectLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendRedirect(String location) throws IOException {
|
||||||
|
redirectLocation = location;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String encodeRedirectURL(String url) {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setStatus(int status) {
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setContentType(String type) {
|
||||||
|
this.contentType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setHeader(String name, String value) {
|
||||||
|
headers.put(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHeader(String name) {
|
||||||
|
return headers.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PrintWriter getWriter() throws IOException {
|
||||||
|
body = new StringWriter();
|
||||||
|
return new PrintWriter(body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue