Permalink
Browse files

OOZIE-1286 SSH Action does not properly handle arguments that have sp…

…aces (rkanter)

git-svn-id: https://svn.apache.org/repos/asf/oozie/trunk@1464683 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information...
1 parent 946dfb2 commit dee79fa8e6ded89bec6216f9d0f7ab1f3ec52145 @rkanter rkanter committed Apr 4, 2013
@@ -1531,6 +1531,8 @@ private void validateCommand(CommandLine commandLine) throws OozieCLIException {
"sqoop-action-0.4.xsd")));
sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
"ssh-action-0.1.xsd")));
+ sources.add(new StreamSource(Thread.currentThread().getContextClassLoader().getResourceAsStream(
+ "ssh-action-0.2.xsd")));
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(sources.toArray(new StreamSource[sources.size()]));
Validator validator = schema.newValidator();
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ xmlns:ssh="uri:oozie:ssh-action:0.2" elementFormDefault="qualified"
+ targetNamespace="uri:oozie:ssh-action:0.2">
+
+ <xs:element name="ssh" type="ssh:ACTION"/>
+
+ <xs:complexType name="ACTION">
+ <xs:sequence>
+ <xs:element name="host" type="xs:string" minOccurs="1" maxOccurs="1"/>
+ <xs:element name="command" type="xs:string" minOccurs="1" maxOccurs="1"/>
+ <xs:choice>
+ <xs:element name="args" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
+ <xs:element name="arg" type="xs:string" minOccurs="0" maxOccurs="unbounded"/>
+ </xs:choice>
+ <xs:element name="capture-output" type="ssh:FLAG" minOccurs="0" maxOccurs="1"/>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:complexType name="FLAG"/>
+
+</xs:schema>
@@ -36,7 +36,7 @@
<property>
<name>oozie.service.SchemaService.wf.ext.schemas</name>
- <value>shell-action-0.1.xsd,shell-action-0.2.xsd,email-action-0.1.xsd,hive-action-0.2.xsd,hive-action-0.3.xsd,sqoop-action-0.2.xsd,sqoop-action-0.3.xsd,ssh-action-0.1.xsd,distcp-action-0.1.xsd</value>
+ <value>shell-action-0.1.xsd,shell-action-0.2.xsd,email-action-0.1.xsd,hive-action-0.2.xsd,hive-action-0.3.xsd,sqoop-action-0.2.xsd,sqoop-action-0.3.xsd,ssh-action-0.1.xsd,ssh-action-0.2.xsd,distcp-action-0.1.xsd</value>
</property>
<property>
@@ -22,8 +22,10 @@
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
+import org.apache.hadoop.util.StringUtils;
import org.apache.oozie.client.WorkflowAction;
import org.apache.oozie.client.OozieClient;
@@ -217,23 +219,47 @@ public String call() throws Exception {
final Element commandElement = conf.getChild("command", nameSpace);
final boolean ignoreOutput = conf.getChild("capture-output", nameSpace) == null;
+ boolean preserve = false;
if (commandElement != null) {
+ String[] args = null;
+ // Will either have <args>, <arg>, or neither (but not both)
List<Element> argsList = conf.getChildren("args", nameSpace);
- StringBuilder args = new StringBuilder("");
- if ((argsList != null) && (argsList.size() > 0)) {
+ // Arguments in an <args> are "flattened" (spaces are delimiters)
+ if (argsList != null && argsList.size() > 0) {
+ StringBuilder argsString = new StringBuilder("");
for (Element argsElement : argsList) {
- args = args.append(argsElement.getValue()).append(" ");
+ argsString = argsString.append(argsElement.getValue()).append(" ");
}
- args.setLength(args.length() - 1);
+ args = new String[]{argsString.toString()};
}
- final String argsString = args.toString();
+ else {
+ // Arguments in an <arg> are preserved, even with spaces
+ argsList = conf.getChildren("arg", nameSpace);
+ if (argsList != null && argsList.size() > 0) {
+ preserve = true;
+ args = new String[argsList.size()];
+ for (int i = 0; i < argsList.size(); i++) {
+ Element argsElement = argsList.get(i);
+ args[i] = argsElement.getValue();
+ // Even though we're keeping the args as an array, if they contain a space we still have to either quote
+ // them or escape their space (because the scripts will split them up otherwise)
+ if (args[i].contains(" ") &&
+ !(args[i].startsWith("\"") && args[i].endsWith("\"") ||
+ args[i].startsWith("'") && args[i].endsWith("'"))) {
+ args[i] = StringUtils.escapeString(args[i], '\\', ' ');
+ }
+ }
+ }
+ }
+ final String[] argsF = args;
final String recoveryId = context.getRecoveryId();
+ final boolean preserveF = preserve;
pid = execute(new Callable<String>() {
@Override
public String call() throws Exception {
- return doExecute(host, dirLocation, commandElement.getValue(), argsString, ignoreOutput,
- action, recoveryId);
+ return doExecute(host, dirLocation, commandElement.getValue(), argsF, ignoreOutput, action, recoveryId,
+ preserveF);
}
});
@@ -364,23 +390,36 @@ protected String setupRemote(String host, Context context, WorkflowAction action
* @param ignoreOutput ignore output option.
* @param action action object.
* @param recoveryId action id + run number to enable recovery in rerun
+ * @param preserveArgs tell the ssh scripts to preserve or flatten the arguments
* @return process id of the running command.
* @throws IOException thrown if failed to run the command.
* @throws InterruptedException thrown if any interruption happens.
*/
- protected String doExecute(String host, String dirLocation, String cmnd, String args, boolean ignoreOutput,
- WorkflowAction action, String recoveryId) throws IOException, InterruptedException {
+ 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 ? "_" : getOozieConf().get(HTTP_COMMAND_OPTIONS).replace(" ", "%%%");
+ String preserveArgsS = preserveArgs ? "PRESERVE_ARGS" : "FLATTEN_ARGS";
// TODO check
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, getOozieConf().get(HTTP_COMMAND), callBackUrl, callbackPost, recoveryId, cmnd, args)
+ String command = XLog.format("{0}{1} {2}ssh-base.sh {3} {4} \"{5}\" \"{6}\" {7} {8} ", SSH_COMMAND_BASE, host, dirLocation,
+ preserveArgsS, getOozieConf().get(HTTP_COMMAND), callBackUrl, callbackPost, recoveryId, cmnd)
.toString();
- log.trace("Executing ssh command [{0}]", command);
- Process p = runtime.exec(command.split("\\s"));
+ String[] commandArray = command.split("\\s");
+ String[] finalCommand;
+ if (args == null) {
+ finalCommand = commandArray;
+ }
+ else {
+ finalCommand = new String[commandArray.length + args.length];
+ System.arraycopy(commandArray, 0, finalCommand, 0, commandArray.length);
+ System.arraycopy(args, 0, finalCommand, commandArray.length, args.length);
+ }
+ log.trace("Executing ssh command [{0}]", Arrays.toString(finalCommand));
+ Process p = runtime.exec(finalCommand);
String pid = "";
StringBuffer inputBuffer = new StringBuffer();
@@ -35,6 +35,13 @@ fi
# exit 127
#fi
-cmnd="$dir/ssh-wrapper.sh ${*}"
-${cmnd} </dev/null >/dev/null 2>&1 &
-echo $!
+preserveArgs=${1}
+if [ $preserveArgs == "PRESERVE_ARGS" ]
+then
+ $dir/ssh-wrapper.sh "${@}" </dev/null >/dev/null 2>&1 &
+ echo $!
+else
+ cmnd="$dir/ssh-wrapper.sh ${*}"
+ ${cmnd} </dev/null >/dev/null 2>&1 &
+ echo $!
+fi
@@ -18,6 +18,8 @@
#
sleep 1
+preserveArgs=${1}
+shift
callbackCmnd=${1}
shift
callbackUrl=${1}
@@ -31,12 +33,25 @@ mpid=`echo $$`
echo $mpid > $dir/$actionId.pid
stdout="$dir/$mpid.$actionId.stdout"
stderr="$dir/$mpid.$actionId.stderr"
-cmnd="${*}"
-if $cmnd >>${stdout} 2>>${stderr}; then
- export callbackUrl=`echo ${callbackUrl} | sed -e 's/#status/OK/'`
+
+if [ $preserveArgs == "PRESERVE_ARGS" ]
+then
+ cmnd=${1}
+ shift
+ if $cmnd "$@" >>${stdout} 2>>${stderr}; then
+ export callbackUrl=`echo ${callbackUrl} | sed -e 's/#status/OK/'`
+ else
+ export callbackUrl=`echo ${callbackUrl} | sed -e 's/#status/ERROR/'`
+ touch $dir/$mpid.$actionId.error
+ fi
else
- export callbackUrl=`echo ${callbackUrl} | sed -e 's/#status/ERROR/'`
- touch $dir/$mpid.$actionId.error
+ cmnd="${*}"
+ if $cmnd >>${stdout} 2>>${stderr}; then
+ export callbackUrl=`echo ${callbackUrl} | sed -e 's/#status/OK/'`
+ else
+ export callbackUrl=`echo ${callbackUrl} | sed -e 's/#status/ERROR/'`
+ touch $dir/$mpid.$actionId.error
+ fi
fi
sleep 1
Oops, something went wrong.

0 comments on commit dee79fa

Please sign in to comment.