YARN-4842. Fixed "yarn logs" command to guess (and thus not require) the appOwner argument when viewing another user's logs. Contributed by Ram Venkatesh and Xuan Gong.

This commit is contained in:
Vinod Kumar Vavilapalli 2016-05-09 22:33:14 -07:00
parent 996a210ab0
commit 87f5e35133
3 changed files with 290 additions and 50 deletions

View File

@ -151,14 +151,12 @@ public int run(String[] args) throws Exception {
LogCLIHelpers logCliHelper = new LogCLIHelpers(); LogCLIHelpers logCliHelper = new LogCLIHelpers();
logCliHelper.setConf(getConf()); logCliHelper.setConf(getConf());
if (appOwner == null || appOwner.isEmpty()) {
appOwner = UserGroupInformation.getCurrentUser().getShortUserName();
}
boolean appStateObtainedSuccessfully = true; boolean appStateObtainedSuccessfully = true;
YarnApplicationState appState = YarnApplicationState.NEW; YarnApplicationState appState = YarnApplicationState.NEW;
ApplicationReport appReport = null;
try { try {
appState = getApplicationState(appId); appReport = getApplicationReport(appId);
appState = appReport.getYarnApplicationState();
if (appState == YarnApplicationState.NEW if (appState == YarnApplicationState.NEW
|| appState == YarnApplicationState.NEW_SAVING || appState == YarnApplicationState.NEW_SAVING
|| appState == YarnApplicationState.SUBMITTED) { || appState == YarnApplicationState.SUBMITTED) {
@ -171,6 +169,16 @@ public int run(String[] args) throws Exception {
+ " Attempting to fetch logs directly from the filesystem."); + " Attempting to fetch logs directly from the filesystem.");
} }
if (appOwner == null || appOwner.isEmpty()) {
appOwner = guessAppOwner(appReport, appId);
if (appOwner == null) {
System.err.println("Can not find the appOwner. "
+ "Please specify the correct appOwner");
System.err.println("Could not locate application logs for " + appId);
return -1;
}
}
if (showMetaInfo) { if (showMetaInfo) {
return showMetaInfo(appState, appStateObtainedSuccessfully, return showMetaInfo(appState, appStateObtainedSuccessfully,
logCliHelper, appId, containerIdStr, nodeAddress, appOwner); logCliHelper, appId, containerIdStr, nodeAddress, appOwner);
@ -201,6 +209,10 @@ public int run(String[] args) throws Exception {
if (nodeAddress == null) { if (nodeAddress == null) {
resultCode = resultCode =
logCliHelper.dumpAllContainersLogs(appId, appOwner, System.out); logCliHelper.dumpAllContainersLogs(appId, appOwner, System.out);
if (resultCode == -1) {
System.err.println("Can not find the logs for the application: "
+ appId + " with the appOwner: " + appOwner);
}
} else { } else {
System.err.println("Should at least provide ContainerId!"); System.err.println("Should at least provide ContainerId!");
printHelpMessage(printOpts); printHelpMessage(printOpts);
@ -210,13 +222,12 @@ public int run(String[] args) throws Exception {
return resultCode; return resultCode;
} }
private YarnApplicationState getApplicationState(ApplicationId appId) private ApplicationReport getApplicationReport(ApplicationId appId)
throws IOException, YarnException { throws IOException, YarnException {
YarnClient yarnClient = createYarnClient(); YarnClient yarnClient = createYarnClient();
try { try {
ApplicationReport appReport = yarnClient.getApplicationReport(appId); return yarnClient.getApplicationReport(appId);
return appReport.getYarnApplicationState();
} finally { } finally {
yarnClient.close(); yarnClient.close();
} }
@ -693,11 +704,12 @@ private int fetchAMContainerLogs(String[] logFiles,
amContainersList, logFiles, logCliHelper, appOwner, true); amContainersList, logFiles, logCliHelper, appOwner, true);
} else { } else {
System.err.println("Can not get AMContainers logs for " System.err.println("Can not get AMContainers logs for "
+ "the application:" + appId); + "the application:" + appId + " with the appOwner:" + appOwner);
System.err.println("This application:" + appId + " is finished." System.err.println("This application:" + appId + " has finished."
+ " Please enable the application history service. Or Using " + " Please enable the application-history service or explicitly"
+ "yarn logs -applicationId <appId> -containerId <containerId> " + " use 'yarn logs -applicationId <appId> "
+ "--nodeAddress <nodeHttpAddress> to get the container logs"); + "-containerId <containerId> --nodeAddress <nodeHttpAddress>' "
+ "to get the container logs.");
return -1; return -1;
} }
} }
@ -750,7 +762,8 @@ private int fetchContainerLogs(YarnApplicationState appState,
appOwner); appOwner);
} else if (!isApplicationFinished(appState)) { } else if (!isApplicationFinished(appState)) {
System.err.println("Unable to get logs for this container:" System.err.println("Unable to get logs for this container:"
+ containerIdStr + "for the application:" + appIdStr); + containerIdStr + "for the application:" + appIdStr
+ " with the appOwner: " + appOwner);
System.err.println("The application: " + appIdStr System.err.println("The application: " + appIdStr
+ " is still running, and we can not get Container report " + " is still running, and we can not get Container report "
+ "for the container: " + containerIdStr +". Please try later " + "for the container: " + containerIdStr +". Please try later "
@ -821,4 +834,18 @@ public boolean isAppFinished() {
return isAppFinished; return isAppFinished;
} }
} }
private String guessAppOwner(ApplicationReport appReport,
ApplicationId appId) throws IOException {
String appOwner = null;
if (appReport != null) {
//always use the app owner from the app report if possible
appOwner = appReport.getUser();
} else {
appOwner = UserGroupInformation.getCurrentUser().getShortUserName();
appOwner = LogCLIHelpers.getOwnerForAppIdOrNull(
appId, appOwner, getConf());
}
return appOwner;
}
} }

View File

@ -42,11 +42,11 @@
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.junit.Assert;
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.LocalFileSystem; import org.apache.hadoop.fs.LocalFileSystem;
import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.permission.FsPermission;
import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.yarn.api.records.ApplicationAccessType; import org.apache.hadoop.yarn.api.records.ApplicationAccessType;
import org.apache.hadoop.yarn.api.records.ApplicationAttemptId; import org.apache.hadoop.yarn.api.records.ApplicationAttemptId;
@ -55,22 +55,21 @@
import org.apache.hadoop.yarn.api.records.ContainerId; import org.apache.hadoop.yarn.api.records.ContainerId;
import org.apache.hadoop.yarn.api.records.NodeId; import org.apache.hadoop.yarn.api.records.NodeId;
import org.apache.hadoop.yarn.api.records.YarnApplicationState; import org.apache.hadoop.yarn.api.records.YarnApplicationState;
import org.apache.hadoop.yarn.api.records.impl.pb.ApplicationAttemptIdPBImpl;
import org.apache.hadoop.yarn.api.records.impl.pb.ApplicationIdPBImpl;
import org.apache.hadoop.yarn.api.records.impl.pb.ContainerIdPBImpl;
import org.apache.hadoop.yarn.client.api.YarnClient; import org.apache.hadoop.yarn.client.api.YarnClient;
import org.apache.hadoop.yarn.conf.YarnConfiguration; import org.apache.hadoop.yarn.conf.YarnConfiguration;
import org.apache.hadoop.yarn.exceptions.YarnException; import org.apache.hadoop.yarn.exceptions.YarnException;
import org.apache.hadoop.yarn.logaggregation.AggregatedLogFormat; import org.apache.hadoop.yarn.logaggregation.AggregatedLogFormat;
import org.apache.hadoop.yarn.logaggregation.LogAggregationUtils; import org.apache.hadoop.yarn.logaggregation.LogAggregationUtils;
import org.apache.hadoop.yarn.logaggregation.LogCLIHelpers; import org.apache.hadoop.yarn.logaggregation.LogCLIHelpers;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
public class TestLogsCLI { public class TestLogsCLI {
ByteArrayOutputStream sysOutStream; ByteArrayOutputStream sysOutStream;
private PrintStream sysOut; private PrintStream sysOut;
ByteArrayOutputStream sysErrStream; ByteArrayOutputStream sysErrStream;
private PrintStream sysErr; private PrintStream sysErr;
@ -79,7 +78,7 @@ public void setUp() {
sysOutStream = new ByteArrayOutputStream(); sysOutStream = new ByteArrayOutputStream();
sysOut = new PrintStream(sysOutStream); sysOut = new PrintStream(sysOutStream);
System.setOut(sysOut); System.setOut(sysOut);
sysErrStream = new ByteArrayOutputStream(); sysErrStream = new ByteArrayOutputStream();
sysErr = new PrintStream(sysErrStream); sysErr = new PrintStream(sysErrStream);
System.setErr(sysErr); System.setErr(sysErr);
@ -91,16 +90,18 @@ public void testFailResultCodes() throws Exception {
conf.setClass("fs.file.impl", LocalFileSystem.class, FileSystem.class); conf.setClass("fs.file.impl", LocalFileSystem.class, FileSystem.class);
LogCLIHelpers cliHelper = new LogCLIHelpers(); LogCLIHelpers cliHelper = new LogCLIHelpers();
cliHelper.setConf(conf); cliHelper.setConf(conf);
YarnClient mockYarnClient = createMockYarnClient(YarnApplicationState.FINISHED); YarnClient mockYarnClient = createMockYarnClient(
YarnApplicationState.FINISHED,
UserGroupInformation.getCurrentUser().getShortUserName());
LogsCLI dumper = new LogsCLIForTest(mockYarnClient); LogsCLI dumper = new LogsCLIForTest(mockYarnClient);
dumper.setConf(conf); dumper.setConf(conf);
// verify dumping a non-existent application's logs returns a failure code // verify dumping a non-existent application's logs returns a failure code
int exitCode = dumper.run( new String[] { int exitCode = dumper.run( new String[] {
"-applicationId", "application_0_0" } ); "-applicationId", "application_0_0" } );
assertTrue("Should return an error code", exitCode != 0); assertTrue("Should return an error code", exitCode != 0);
// verify dumping a non-existent container log is a failure code // verify dumping a non-existent container log is a failure code
exitCode = cliHelper.dumpAContainersLogs("application_0_0", "container_0_0", exitCode = cliHelper.dumpAContainersLogs("application_0_0", "container_0_0",
"nonexistentnode:1234", "nobody"); "nonexistentnode:1234", "nobody");
assertTrue("Should return an error code", exitCode != 0); assertTrue("Should return an error code", exitCode != 0);
@ -109,10 +110,12 @@ public void testFailResultCodes() throws Exception {
@Test(timeout = 5000l) @Test(timeout = 5000l)
public void testInvalidApplicationId() throws Exception { public void testInvalidApplicationId() throws Exception {
Configuration conf = new YarnConfiguration(); Configuration conf = new YarnConfiguration();
YarnClient mockYarnClient = createMockYarnClient(YarnApplicationState.FINISHED); YarnClient mockYarnClient = createMockYarnClient(
YarnApplicationState.FINISHED,
UserGroupInformation.getCurrentUser().getShortUserName());
LogsCLI cli = new LogsCLIForTest(mockYarnClient); LogsCLI cli = new LogsCLIForTest(mockYarnClient);
cli.setConf(conf); cli.setConf(conf);
int exitCode = cli.run( new String[] { "-applicationId", "not_an_app_id"}); int exitCode = cli.run( new String[] { "-applicationId", "not_an_app_id"});
assertTrue(exitCode == -1); assertTrue(exitCode == -1);
assertTrue(sysErrStream.toString().startsWith("Invalid ApplicationId specified")); assertTrue(sysErrStream.toString().startsWith("Invalid ApplicationId specified"));
@ -137,7 +140,9 @@ public void testUnknownApplicationId() throws Exception {
@Test(timeout = 5000l) @Test(timeout = 5000l)
public void testHelpMessage() throws Exception { public void testHelpMessage() throws Exception {
Configuration conf = new YarnConfiguration(); Configuration conf = new YarnConfiguration();
YarnClient mockYarnClient = createMockYarnClient(YarnApplicationState.FINISHED); YarnClient mockYarnClient = createMockYarnClient(
YarnApplicationState.FINISHED,
UserGroupInformation.getCurrentUser().getShortUserName());
LogsCLI dumper = new LogsCLIForTest(mockYarnClient); LogsCLI dumper = new LogsCLIForTest(mockYarnClient);
dumper.setConf(conf); dumper.setConf(conf);
@ -187,7 +192,7 @@ public void testHelpMessage() throws Exception {
String appReportStr = baos.toString("UTF-8"); String appReportStr = baos.toString("UTF-8");
Assert.assertEquals(appReportStr, sysOutStream.toString()); Assert.assertEquals(appReportStr, sysOutStream.toString());
} }
@Test (timeout = 15000) @Test (timeout = 15000)
public void testFetchApplictionLogs() throws Exception { public void testFetchApplictionLogs() throws Exception {
String remoteLogRootDir = "target/logs/"; String remoteLogRootDir = "target/logs/";
@ -200,13 +205,13 @@ public void testFetchApplictionLogs() throws Exception {
FileSystem fs = FileSystem.get(configuration); FileSystem fs = FileSystem.get(configuration);
UserGroupInformation ugi = UserGroupInformation.getCurrentUser(); UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
ApplicationId appId = ApplicationIdPBImpl.newInstance(0, 1); ApplicationId appId = ApplicationId.newInstance(0, 1);
ApplicationAttemptId appAttemptId = ApplicationAttemptId appAttemptId =
ApplicationAttemptIdPBImpl.newInstance(appId, 1); ApplicationAttemptId.newInstance(appId, 1);
ContainerId containerId0 = ContainerIdPBImpl.newContainerId(appAttemptId, 0); ContainerId containerId0 = ContainerId.newContainerId(appAttemptId, 0);
ContainerId containerId1 = ContainerIdPBImpl.newContainerId(appAttemptId, 1); ContainerId containerId1 = ContainerId.newContainerId(appAttemptId, 1);
ContainerId containerId2 = ContainerIdPBImpl.newContainerId(appAttemptId, 2); ContainerId containerId2 = ContainerId.newContainerId(appAttemptId, 2);
ContainerId containerId3 = ContainerIdPBImpl.newContainerId(appAttemptId, 3); ContainerId containerId3 = ContainerId.newContainerId(appAttemptId, 3);
NodeId nodeId = NodeId.newInstance("localhost", 1234); NodeId nodeId = NodeId.newInstance("localhost", 1234);
// create local logs // create local logs
@ -222,6 +227,7 @@ public void testFetchApplictionLogs() throws Exception {
fs.delete(appLogsDir, true); fs.delete(appLogsDir, true);
} }
assertTrue(fs.mkdirs(appLogsDir)); assertTrue(fs.mkdirs(appLogsDir));
List<String> rootLogDirs = Arrays.asList(rootLogDir); List<String> rootLogDirs = Arrays.asList(rootLogDir);
List<String> logTypes = new ArrayList<String>(); List<String> logTypes = new ArrayList<String>();
@ -258,7 +264,8 @@ public void testFetchApplictionLogs() throws Exception {
containerId3, path, fs); containerId3, path, fs);
YarnClient mockYarnClient = YarnClient mockYarnClient =
createMockYarnClient(YarnApplicationState.FINISHED); createMockYarnClient(
YarnApplicationState.FINISHED, ugi.getShortUserName());
LogsCLI cli = new LogsCLIForTest(mockYarnClient); LogsCLI cli = new LogsCLIForTest(mockYarnClient);
cli.setConf(configuration); cli.setConf(configuration);
@ -348,6 +355,143 @@ public void testFetchApplictionLogs() throws Exception {
fs.delete(new Path(rootLogDir), true); fs.delete(new Path(rootLogDir), true);
} }
@Test (timeout = 15000)
public void testFetchApplictionLogsAsAnotherUser() throws Exception {
String remoteLogRootDir = "target/logs/";
String rootLogDir = "target/LocalLogs";
String testUser = "test";
UserGroupInformation testUgi = UserGroupInformation
.createRemoteUser(testUser);
Configuration configuration = new Configuration();
configuration.setBoolean(YarnConfiguration.LOG_AGGREGATION_ENABLED, true);
configuration
.set(YarnConfiguration.NM_REMOTE_APP_LOG_DIR, remoteLogRootDir);
configuration.setBoolean(YarnConfiguration.YARN_ACL_ENABLE, true);
configuration.set(YarnConfiguration.YARN_ADMIN_ACL, "admin");
FileSystem fs = FileSystem.get(configuration);
ApplicationId appId = ApplicationId.newInstance(0, 1);
ApplicationAttemptId appAttemptId =
ApplicationAttemptId.newInstance(appId, 1);
ContainerId containerId = ContainerId
.newContainerId(appAttemptId, 1);
NodeId nodeId = NodeId.newInstance("localhost", 1234);
try {
Path rootLogDirPath = new Path(rootLogDir);
if (fs.exists(rootLogDirPath)) {
fs.delete(rootLogDirPath, true);
}
assertTrue(fs.mkdirs(rootLogDirPath));
// create local app dir for app
final Path appLogsDir = new Path(rootLogDirPath, appId.toString());
if (fs.exists(appLogsDir)) {
fs.delete(appLogsDir, true);
}
assertTrue(fs.mkdirs(appLogsDir));
List<String> rootLogDirs = Arrays.asList(rootLogDir);
List<String> logTypes = new ArrayList<String>();
logTypes.add("syslog");
// create container logs in localLogDir for app
createContainerLogInLocalDir(appLogsDir, containerId, fs, logTypes);
// create the remote app dir for app
// but for a different user testUser"
Path path = new Path(remoteLogRootDir + testUser + "/logs/" + appId);
if (fs.exists(path)) {
fs.delete(path, true);
}
assertTrue(fs.mkdirs(path));
// upload container logs for app into remote dir
uploadContainerLogIntoRemoteDir(testUgi, configuration, rootLogDirs,
nodeId, containerId, path, fs);
YarnClient mockYarnClient = createMockYarnClient(
YarnApplicationState.FINISHED, testUgi.getShortUserName());
LogsCLI cli = new LogsCLIForTest(mockYarnClient);
cli.setConf(configuration);
// Verify that we can get the application logs by specifying
// a correct appOwner
int exitCode = cli.run(new String[] {
"-applicationId", appId.toString(),
"-appOwner", testUser});
assertTrue(exitCode == 0);
assertTrue(sysOutStream.toString().contains(
"Hello " + containerId + " in syslog!"));
sysOutStream.reset();
// Verify that we can not get the application logs
// if an invalid user is specified
exitCode = cli.run(new String[] {
"-applicationId", appId.toString(),
"-appOwner", "invalid"});
assertTrue(exitCode == -1);
assertTrue(sysErrStream.toString().contains("Can not find the logs "
+ "for the application: " + appId.toString()));
sysErrStream.reset();
// Verify that we do not specify appOwner, and can not
// get appReport from RM, we still can figure out the appOwner
// and can get app logs successfully.
YarnClient mockYarnClient2 = createMockYarnClientUnknownApp();
cli = new LogsCLIForTest(mockYarnClient2);
cli.setConf(configuration);
exitCode = cli.run(new String[] {
"-applicationId", appId.toString()});
assertTrue(exitCode == 0);
assertTrue(sysOutStream.toString().contains("Hello "
+ containerId + " in syslog!"));
sysOutStream.reset();
// Verify that we could get the err message "Can not find the appOwner"
// if we do not specify the appOwner, can not get appReport, and
// the app does not exist in remote dir.
ApplicationId appId2 = ApplicationId.newInstance(
System.currentTimeMillis(), 2);
exitCode = cli.run(new String[] {
"-applicationId", appId2.toString()});
assertTrue(exitCode == -1);
assertTrue(sysErrStream.toString().contains(
"Can not find the appOwner"));
sysErrStream.reset();
// Verify that we could not get appOwner
// because we don't have file-system permissions
ApplicationId appTest = ApplicationId.newInstance(
System.currentTimeMillis(), 1000);
String priorityUser = "priority";
Path pathWithoutPerm = new Path(remoteLogRootDir + priorityUser
+ "/logs/" + appTest);
if (fs.exists(pathWithoutPerm)) {
fs.delete(pathWithoutPerm, true);
}
// The user will not have read permission for this directory.
// To mimic the scenario that the user can not get file status
FsPermission permission = FsPermission
.createImmutable((short) 01300);
assertTrue(fs.mkdirs(pathWithoutPerm, permission));
exitCode = cli.run(new String[] {
"-applicationId", appTest.toString()});
assertTrue(exitCode == -1);
assertTrue(sysErrStream.toString().contains(
"Guessed logs' owner is " + priorityUser + " and current user "
+ UserGroupInformation.getCurrentUser().getUserName()
+ " does not have permission to access"));
sysErrStream.reset();
} finally {
fs.delete(new Path(remoteLogRootDir), true);
fs.delete(new Path(rootLogDir), true);
}
}
@Test (timeout = 15000) @Test (timeout = 15000)
public void testPrintContainerLogMetadata() throws Exception { public void testPrintContainerLogMetadata() throws Exception {
String remoteLogRootDir = "target/logs/"; String remoteLogRootDir = "target/logs/";
@ -380,7 +524,8 @@ public void testPrintContainerLogMetadata() throws Exception {
appId, containerIds, nodeIds); appId, containerIds, nodeIds);
YarnClient mockYarnClient = YarnClient mockYarnClient =
createMockYarnClient(YarnApplicationState.FINISHED); createMockYarnClient(YarnApplicationState.FINISHED,
UserGroupInformation.getCurrentUser().getShortUserName());
LogsCLI cli = new LogsCLIForTest(mockYarnClient); LogsCLI cli = new LogsCLIForTest(mockYarnClient);
cli.setConf(configuration); cli.setConf(configuration);
@ -466,7 +611,8 @@ public void testListNodeInfo() throws Exception {
appId, containerIds, nodeIds); appId, containerIds, nodeIds);
YarnClient mockYarnClient = YarnClient mockYarnClient =
createMockYarnClient(YarnApplicationState.FINISHED); createMockYarnClient(YarnApplicationState.FINISHED,
UserGroupInformation.getCurrentUser().getShortUserName());
LogsCLI cli = new LogsCLIForTest(mockYarnClient); LogsCLI cli = new LogsCLIForTest(mockYarnClient);
cli.setConf(configuration); cli.setConf(configuration);
@ -508,7 +654,8 @@ public void testFetchApplictionLogsHar() throws Exception {
assertTrue(fs.exists(harPath)); assertTrue(fs.exists(harPath));
YarnClient mockYarnClient = YarnClient mockYarnClient =
createMockYarnClient(YarnApplicationState.FINISHED); createMockYarnClient(YarnApplicationState.FINISHED,
ugi.getShortUserName());
LogsCLI cli = new LogsCLIForTest(mockYarnClient); LogsCLI cli = new LogsCLIForTest(mockYarnClient);
cli.setConf(configuration); cli.setConf(configuration);
int exitCode = cli.run(new String[]{"-applicationId", int exitCode = cli.run(new String[]{"-applicationId",
@ -630,10 +777,12 @@ private static void uploadEmptyContainerLogIntoRemoteDir(UserGroupInformation ug
writer.close(); writer.close();
} }
private YarnClient createMockYarnClient(YarnApplicationState appState) private YarnClient createMockYarnClient(YarnApplicationState appState,
String user)
throws YarnException, IOException { throws YarnException, IOException {
YarnClient mockClient = mock(YarnClient.class); YarnClient mockClient = mock(YarnClient.class);
ApplicationReport mockAppReport = mock(ApplicationReport.class); ApplicationReport mockAppReport = mock(ApplicationReport.class);
doReturn(user).when(mockAppReport).getUser();
doReturn(appState).when(mockAppReport).getYarnApplicationState(); doReturn(appState).when(mockAppReport).getYarnApplicationState();
doReturn(mockAppReport).when(mockClient).getApplicationReport( doReturn(mockAppReport).when(mockClient).getApplicationReport(
any(ApplicationId.class)); any(ApplicationId.class));
@ -659,9 +808,9 @@ private YarnClient createMockYarnClientUnknownApp() throws YarnException,
} }
private static class LogsCLIForTest extends LogsCLI { private static class LogsCLIForTest extends LogsCLI {
private YarnClient yarnClient; private YarnClient yarnClient;
public LogsCLIForTest(YarnClient yarnClient) { public LogsCLIForTest(YarnClient yarnClient) {
super(); super();
this.yarnClient = yarnClient; this.yarnClient = yarnClient;

View File

@ -23,6 +23,7 @@
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream; import java.io.PrintStream;
import java.nio.file.AccessDeniedException;
import java.util.List; import java.util.List;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
@ -34,6 +35,8 @@
import org.apache.hadoop.fs.HarFs; import org.apache.hadoop.fs.HarFs;
import org.apache.hadoop.fs.Path; import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.RemoteIterator; import org.apache.hadoop.fs.RemoteIterator;
import org.apache.hadoop.security.AccessControlException;
import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.yarn.api.records.ApplicationId; import org.apache.hadoop.yarn.api.records.ApplicationId;
import org.apache.hadoop.yarn.conf.YarnConfiguration; import org.apache.hadoop.yarn.conf.YarnConfiguration;
import org.apache.hadoop.yarn.logaggregation.AggregatedLogFormat.LogKey; import org.apache.hadoop.yarn.logaggregation.AggregatedLogFormat.LogKey;
@ -54,6 +57,53 @@ public int dumpAContainersLogs(String appId, String containerId,
null); null);
} }
@Private
@VisibleForTesting
/**
* Return the owner for a given AppId
* @param remoteRootLogDir
* @param appId
* @param bestGuess
* @param conf
* @return the owner or null
* @throws IOException
*/
public static String getOwnerForAppIdOrNull(
ApplicationId appId, String bestGuess,
Configuration conf) throws IOException {
Path remoteRootLogDir = new Path(conf.get(
YarnConfiguration.NM_REMOTE_APP_LOG_DIR,
YarnConfiguration.DEFAULT_NM_REMOTE_APP_LOG_DIR));
String suffix = LogAggregationUtils.getRemoteNodeLogDirSuffix(conf);
Path fullPath = LogAggregationUtils.getRemoteAppLogDir(remoteRootLogDir,
appId, bestGuess, suffix);
FileContext fc =
FileContext.getFileContext(remoteRootLogDir.toUri(), conf);
String pathAccess = fullPath.toString();
try {
if (fc.util().exists(fullPath)) {
return bestGuess;
}
Path toMatch = LogAggregationUtils.
getRemoteAppLogDir(remoteRootLogDir, appId, "*", suffix);
pathAccess = toMatch.toString();
FileStatus[] matching = fc.util().globStatus(toMatch);
if (matching == null || matching.length != 1) {
return null;
}
//fetch the user from the full path /app-logs/user[/suffix]/app_id
Path parent = matching[0].getPath().getParent();
//skip the suffix too
if (suffix != null && !StringUtils.isEmpty(suffix)) {
parent = parent.getParent();
}
return parent.getName();
} catch (AccessControlException | AccessDeniedException ex) {
logDirNoAccessPermission(pathAccess, bestGuess, ex.getMessage());
return null;
}
}
@Private @Private
@VisibleForTesting @VisibleForTesting
public int dumpAContainersLogsForALogType(String appId, String containerId, public int dumpAContainersLogsForALogType(String appId, String containerId,
@ -93,12 +143,12 @@ public int dumpAContainersLogsForALogType(String appId, String containerId,
thisNodeFile.getPath()); thisNodeFile.getPath());
if (logType == null) { if (logType == null) {
if (dumpAContainerLogs(containerId, reader, System.out, if (dumpAContainerLogs(containerId, reader, System.out,
thisNodeFile.getModificationTime()) > -1) { thisNodeFile.getModificationTime()) > -1) {
foundContainerLogs = true; foundContainerLogs = true;
} }
} else { } else {
if (dumpAContainerLogsForALogType(containerId, reader, System.out, if (dumpAContainerLogsForALogType(containerId, reader, System.out,
thisNodeFile.getModificationTime(), logType) > -1) { thisNodeFile.getModificationTime(), logType) > -1) {
foundContainerLogs = true; foundContainerLogs = true;
} }
} }
@ -182,7 +232,7 @@ public int dumpAContainerLogs(String containerIdStr,
while (true) { while (true) {
try { try {
LogReader.readAContainerLogsForALogType(valueStream, out, LogReader.readAContainerLogsForALogType(valueStream, out,
logUploadedTime); logUploadedTime);
foundContainerLogs = true; foundContainerLogs = true;
} catch (EOFException eof) { } catch (EOFException eof) {
break; break;
@ -249,9 +299,10 @@ public int dumpAllContainersLogs(ApplicationId appId, String appOwner,
continue; continue;
} }
if (!thisNodeFile.getPath().getName() if (!thisNodeFile.getPath().getName()
.endsWith(LogAggregationUtils.TMP_FILE_SUFFIX)) { .endsWith(LogAggregationUtils.TMP_FILE_SUFFIX)) {
AggregatedLogFormat.LogReader reader = AggregatedLogFormat.LogReader reader =
new AggregatedLogFormat.LogReader(getConf(), thisNodeFile.getPath()); new AggregatedLogFormat.LogReader(getConf(),
thisNodeFile.getPath());
try { try {
DataInputStream valueStream; DataInputStream valueStream;
@ -261,13 +312,14 @@ public int dumpAllContainersLogs(ApplicationId appId, String appOwner,
while (valueStream != null) { while (valueStream != null) {
String containerString = String containerString =
"\n\nContainer: " + key + " on " + thisNodeFile.getPath().getName(); "\n\nContainer: " + key + " on "
+ thisNodeFile.getPath().getName();
out.println(containerString); out.println(containerString);
out.println(StringUtils.repeat("=", containerString.length())); out.println(StringUtils.repeat("=", containerString.length()));
while (true) { while (true) {
try { try {
LogReader.readAContainerLogsForALogType(valueStream, out, LogReader.readAContainerLogsForALogType(valueStream, out,
thisNodeFile.getModificationTime()); thisNodeFile.getModificationTime());
foundAnyLogs = true; foundAnyLogs = true;
} catch (EOFException eof) { } catch (EOFException eof) {
break; break;
@ -283,7 +335,7 @@ public int dumpAllContainersLogs(ApplicationId appId, String appOwner,
} }
} }
} }
if (! foundAnyLogs) { if (!foundAnyLogs) {
emptyLogDir(getRemoteAppLogDir(appId, appOwner).toString()); emptyLogDir(getRemoteAppLogDir(appId, appOwner).toString());
return -1; return -1;
} }
@ -398,6 +450,9 @@ private RemoteIterator<FileStatus> getRemoteNodeFileDir(ApplicationId appId,
getConf()).listStatus(remoteAppLogDir); getConf()).listStatus(remoteAppLogDir);
} catch (FileNotFoundException fnf) { } catch (FileNotFoundException fnf) {
logDirNotExist(remoteAppLogDir.toString()); logDirNotExist(remoteAppLogDir.toString());
} catch (AccessControlException | AccessDeniedException ace) {
logDirNoAccessPermission(remoteAppLogDir.toString(), appOwner,
ace.getMessage());
} }
return nodeFiles; return nodeFiles;
} }
@ -426,7 +481,7 @@ public Configuration getConf() {
private static void containerLogNotFound(String containerId) { private static void containerLogNotFound(String containerId) {
System.err.println("Logs for container " + containerId System.err.println("Logs for container " + containerId
+ " are not present in this log-file."); + " are not present in this log-file.");
} }
private static void logDirNotExist(String remoteAppLogDir) { private static void logDirNotExist(String remoteAppLogDir) {
@ -437,4 +492,13 @@ private static void logDirNotExist(String remoteAppLogDir) {
private static void emptyLogDir(String remoteAppLogDir) { private static void emptyLogDir(String remoteAppLogDir) {
System.err.println(remoteAppLogDir + " does not have any log files."); System.err.println(remoteAppLogDir + " does not have any log files.");
} }
private static void logDirNoAccessPermission(String remoteAppLogDir,
String appOwner, String errorMessage) throws IOException {
System.err.println("Guessed logs' owner is " + appOwner
+ " and current user "
+ UserGroupInformation.getCurrentUser().getUserName() + " does not "
+ "have permission to access " + remoteAppLogDir
+ ". Error message found: " + errorMessage);
}
} }