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:
parent
996a210ab0
commit
87f5e35133
|
@ -151,14 +151,12 @@ public class LogsCLI extends Configured implements Tool {
|
||||||
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 class LogsCLI extends Configured implements Tool {
|
||||||
+ " 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 class LogsCLI extends Configured implements Tool {
|
||||||
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 class LogsCLI extends Configured implements Tool {
|
||||||
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 @@ public class LogsCLI extends Configured implements Tool {
|
||||||
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 @@ public class LogsCLI extends Configured implements Tool {
|
||||||
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 class LogsCLI extends Configured implements Tool {
|
||||||
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,11 +42,11 @@ import java.util.HashSet;
|
||||||
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.ApplicationReport;
|
||||||
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 class TestLogsCLI {
|
||||||
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 class TestLogsCLI {
|
||||||
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 class TestLogsCLI {
|
||||||
@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 class TestLogsCLI {
|
||||||
@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 class TestLogsCLI {
|
||||||
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 class TestLogsCLI {
|
||||||
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 class TestLogsCLI {
|
||||||
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 class TestLogsCLI {
|
||||||
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 class TestLogsCLI {
|
||||||
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 class TestLogsCLI {
|
||||||
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 class TestLogsCLI {
|
||||||
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 class TestLogsCLI {
|
||||||
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 @@ public class TestLogsCLI {
|
||||||
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 @@ public class TestLogsCLI {
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
|
|
@ -23,6 +23,7 @@ import java.io.EOFException;
|
||||||
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.FileStatus;
|
||||||
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 class LogCLIHelpers implements Configurable {
|
||||||
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 class LogCLIHelpers implements Configurable {
|
||||||
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 class LogCLIHelpers implements Configurable {
|
||||||
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 class LogCLIHelpers implements Configurable {
|
||||||
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 class LogCLIHelpers implements Configurable {
|
||||||
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 class LogCLIHelpers implements Configurable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (! foundAnyLogs) {
|
if (!foundAnyLogs) {
|
||||||
emptyLogDir(getRemoteAppLogDir(appId, appOwner).toString());
|
emptyLogDir(getRemoteAppLogDir(appId, appOwner).toString());
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -398,6 +450,9 @@ public class LogCLIHelpers implements Configurable {
|
||||||
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 class LogCLIHelpers implements Configurable {
|
||||||
|
|
||||||
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 @@ public class LogCLIHelpers implements Configurable {
|
||||||
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue