OOZIE-3405 SSH action shows empty error Message and Error code (matijhs via asalamon74)
diff --git a/core/src/main/java/org/apache/oozie/ErrorCode.java b/core/src/main/java/org/apache/oozie/ErrorCode.java
index 6b0ce47..8e98535 100644
--- a/core/src/main/java/org/apache/oozie/ErrorCode.java
+++ b/core/src/main/java/org/apache/oozie/ErrorCode.java
@@ -228,6 +228,8 @@
E1102(XLog.STD, "Invalid operation [{0}] for bulk command"),
+ E1111(XLog.STD, "Script failed on remote host with [{0}]"),
+
E1201(XLog.STD, "State [{0}] is invalid for job [{1}]."),
E1301(XLog.STD, "Could not read the bundle job definition, [{0}]"),
diff --git a/core/src/main/java/org/apache/oozie/action/ssh/SshActionExecutor.java b/core/src/main/java/org/apache/oozie/action/ssh/SshActionExecutor.java
index 6956cba..fbc94f1 100644
--- a/core/src/main/java/org/apache/oozie/action/ssh/SshActionExecutor.java
+++ b/core/src/main/java/org/apache/oozie/action/ssh/SshActionExecutor.java
@@ -30,6 +30,7 @@
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.apache.hadoop.util.StringUtils;
+import org.apache.oozie.ErrorCode;
import org.apache.oozie.client.WorkflowAction;
import org.apache.oozie.client.OozieClient;
import org.apache.oozie.client.WorkflowAction.Status;
@@ -144,21 +145,7 @@
String dataCommand = SSH_COMMAND_BASE + action.getTrackerUri() + " cat " + outFile;
LOG.debug("Ssh command [{0}]", dataCommand);
try {
- final Process process = Runtime.getRuntime().exec(dataCommand.split("\\s"));
- final BufferDrainer bufferDrainer = new BufferDrainer(process, maxLen);
- bufferDrainer.drainBuffers();
- final StringBuffer outBuffer = bufferDrainer.getInputBuffer();
- final StringBuffer errBuffer = bufferDrainer.getErrorBuffer();
- boolean overflow = false;
- LOG.trace("outBuffer={0}", outBuffer);
- LOG.trace("errBuffer={0}", errBuffer);
- if (outBuffer.length() > maxLen) {
- overflow = true;
- }
- if (overflow) {
- throw new ActionExecutorException(ActionExecutorException.ErrorType.ERROR,
- "ERR_OUTPUT_EXCEED_MAX_LEN", "unknown error");
- }
+ final StringBuffer outBuffer = getActionOutputMessage(dataCommand);
context.setExecutionData(status.toString(), PropertiesUtils.stringToProperties(outBuffer.toString()));
LOG.trace("Execution data set. status={0}, properties={1}", status,
PropertiesUtils.stringToProperties(outBuffer.toString()));
@@ -177,6 +164,9 @@
if (status == Status.ERROR) {
LOG.warn("Execution data set to null in ERROR");
context.setExecutionData(status.toString(), null);
+ String actionErrorMsg = getActionErrorMessage(context, action);
+ LOG.warn("{0}: Script failed on remote host with [{1}]", ErrorCode.E1111, actionErrorMsg);
+ context.setErrorInfo(ErrorCode.E1111.toString(), actionErrorMsg);
}
else {
LOG.warn("Execution data not set");
@@ -186,6 +176,40 @@
LOG.trace("check() end for action={0}", action);
}
+ private StringBuffer getActionOutputMessage(String dataCommand) throws IOException, ActionExecutorException {
+ final Process process = Runtime.getRuntime().exec(dataCommand.split("\\s"));
+ boolean overflow = false;
+ final BufferDrainer bufferDrainer = new BufferDrainer(process, maxLen);
+ bufferDrainer.drainBuffers();
+ final StringBuffer outBuffer = bufferDrainer.getInputBuffer();
+ final StringBuffer errBuffer = bufferDrainer.getErrorBuffer();
+ LOG.debug("outBuffer={0}", outBuffer);
+ LOG.debug("errBuffer={0}", errBuffer);
+ if (outBuffer.length() > maxLen) {
+ overflow = true;
+ }
+ if (overflow) {
+ throw new ActionExecutorException(ActionExecutorException.ErrorType.ERROR,
+ "ERR_OUTPUT_EXCEED_MAX_LEN", "unknown error");
+ }
+ return outBuffer;
+ }
+
+ private String getActionErrorMessage(Context context, WorkflowAction action) throws ActionExecutorException {
+ String outFile = getRemoteFileName(context, action, "error", false, true);
+ String errorMsgCmd = SSH_COMMAND_BASE + action.getTrackerUri() + " cat " + outFile;
+ LOG.debug("Get error message command: [{0}]", errorMsgCmd);
+ String errorMessage;
+ try {
+ final StringBuffer outBuffer = getActionOutputMessage(errorMsgCmd);
+ errorMessage = outBuffer.toString().replaceAll("\n", "");
+ } catch (Exception ex) {
+ throw new ActionExecutorException(ActionExecutorException.ErrorType.ERROR, "ERR_UNKNOWN_ERROR",
+ "unknown error", ex);
+ }
+ return errorMessage;
+ }
+
/**
* Kill ssh action.
*
@@ -423,7 +447,6 @@
protected String doExecute(String host, String dirLocation, String cmnd, String[] args, boolean ignoreOutput,
WorkflowAction action, String recoveryId, boolean preserveArgs)
throws IOException, InterruptedException {
- XLog log = XLog.getLog(getClass());
Runtime runtime = Runtime.getRuntime();
String callbackPost = ignoreOutput ? "_" : ConfigurationService.get(HTTP_COMMAND_OPTIONS).replace(" ", "%%%");
String preserveArgsS = preserveArgs ? "PRESERVE_ARGS" : "FLATTEN_ARGS";
@@ -431,8 +454,7 @@
String callBackUrl = Services.get().get(CallbackService.class)
.createCallBackUrl(action.getId(), EXT_STATUS_VAR);
String command = XLog.format("{0}{1} {2}ssh-base.sh {3} {4} \"{5}\" \"{6}\" {7} {8} ", SSH_COMMAND_BASE, host, dirLocation,
- preserveArgsS, ConfigurationService.get(HTTP_COMMAND), callBackUrl, callbackPost, recoveryId, cmnd)
- .toString();
+ preserveArgsS, ConfigurationService.get(HTTP_COMMAND), callBackUrl, callbackPost, recoveryId, cmnd);
String[] commandArray = command.split("\\s");
String[] finalCommand;
if (args == null) {
diff --git a/core/src/main/resources/ssh-wrapper.sh b/core/src/main/resources/ssh-wrapper.sh
index e2e6f7f..4bd6b8c 100644
--- a/core/src/main/resources/ssh-wrapper.sh
+++ b/core/src/main/resources/ssh-wrapper.sh
@@ -33,6 +33,8 @@
echo $mpid > $dir/$actionId.pid
stdout="$dir/$mpid.$actionId.stdout"
stderr="$dir/$mpid.$actionId.stderr"
+errorFile="$dir/$mpid.$actionId.error"
+exitCodeMsg="Exit code:"
if [ $preserveArgs == "PRESERVE_ARGS" ]
then
@@ -41,16 +43,18 @@
if $cmnd "$@" >>${stdout} 2>>${stderr}; then
export callbackUrl=`echo ${callbackUrl} | sed -e 's/#status/OK/'`
else
+ ec=$?
export callbackUrl=`echo ${callbackUrl} | sed -e 's/#status/ERROR/'`
- touch $dir/$mpid.$actionId.error
+ echo $exitCodeMsg$ec > $errorFile
fi
else
cmnd="${*}"
if $cmnd >>${stdout} 2>>${stderr}; then
export callbackUrl=`echo ${callbackUrl} | sed -e 's/#status/OK/'`
else
+ ec=$?
export callbackUrl=`echo ${callbackUrl} | sed -e 's/#status/ERROR/'`
- touch $dir/$mpid.$actionId.error
+ echo $exitCodeMsg$ec > $errorFile
fi
fi
sleep 1
diff --git a/core/src/test/java/org/apache/oozie/action/ssh/TestSshActionExecutor.java b/core/src/test/java/org/apache/oozie/action/ssh/TestSshActionExecutor.java
index d68aed0..a5e0fbe 100644
--- a/core/src/test/java/org/apache/oozie/action/ssh/TestSshActionExecutor.java
+++ b/core/src/test/java/org/apache/oozie/action/ssh/TestSshActionExecutor.java
@@ -32,6 +32,7 @@
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
+import org.apache.oozie.ErrorCode;
import org.apache.oozie.WorkflowActionBean;
import org.apache.oozie.WorkflowJobBean;
import org.apache.oozie.action.ActionExecutor;
@@ -154,6 +155,7 @@
@Override
public void setErrorInfo(String str, String exMsg) {
+ action.setErrorInfo(str, exMsg);
}
}
@@ -603,6 +605,36 @@
verify(contextMock).setEndData(WorkflowAction.Status.KILLED, "ERROR");
}
+ /**
+ * test {@code SshActionExecutor.check()} where the remote executable returns an error code
+ */
+ public void testCaptureActionErrorStream() throws Exception {
+ WorkflowJobBean workflow = createBaseWorkflowJobBean();
+ final WorkflowActionBean action = new WorkflowActionBean();
+ action.setId("actionId");
+ action.setConf("<ssh xmlns='" + getActionXMLSchema() + "'>" +
+ "<host>localhost</host>" +
+ "<command>exit 123</command>" +
+ "<capture-output/>" +
+ "</ssh>");
+ action.setName("ssh");
+ final SshActionExecutor ssh = new SshActionExecutor();
+ final Context context = new Context(workflow, action);
+ ssh.start(context, action);
+
+ waitFor(30 * 1000, new Predicate() {
+ public boolean evaluate() throws Exception {
+ ssh.check(context, action);
+ return Status.DONE == action.getStatus();
+ }
+ });
+ ssh.end(context, action);
+ assertEquals("Action status is not ERROR", Status.ERROR, action.getStatus());
+ assertEquals("Error code is not what expected", ErrorCode.E1111.toString(), action.getErrorCode());
+ assertEquals("The remote executable exit code and action error message is not what expected",
+ "Exit code:123", action.getErrorMessage());
+ }
+
@Override
protected void tearDown() throws Exception {
if (!isSshPresent()) {
diff --git a/release-log.txt b/release-log.txt
index 550debf..c5cbc5f 100644
--- a/release-log.txt
+++ b/release-log.txt
@@ -1,5 +1,6 @@
-- Oozie 5.2.0 release (trunk - unreleased)
+OOZIE-3405 SSH action shows empty error Message and Error code (matijhs via asalamon74)
OOZIE-3542 Handle better old Hdfs implementations in ECPolicyDisabler (zsombor dionusos via kmarton)
OOZIE-3540 Use StringBuilder instead of StringBuffer if concurrent access is not required (zsombor via asalamon74)
OOZIE-3539 amend Support http proxy/basic authentication in the command line client (zsombor via asalamon74)