You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@oozie.apache.org by rk...@apache.org on 2013/04/04 21:11:21 UTC

svn commit: r1464683 - in /oozie/trunk: ./ client/src/main/java/org/apache/oozie/cli/ client/src/main/resources/ core/src/main/conf/ core/src/main/java/org/apache/oozie/action/ssh/ core/src/main/resources/ core/src/test/java/org/apache/oozie/action/ssh...

Author: rkanter
Date: Thu Apr  4 19:11:20 2013
New Revision: 1464683

URL: http://svn.apache.org/r1464683
Log:
OOZIE-1286 SSH Action does not properly handle arguments that have spaces (rkanter)

Added:
    oozie/trunk/client/src/main/resources/ssh-action-0.2.xsd
Modified:
    oozie/trunk/client/src/main/java/org/apache/oozie/cli/OozieCLI.java
    oozie/trunk/core/src/main/conf/oozie-site.xml
    oozie/trunk/core/src/main/java/org/apache/oozie/action/ssh/SshActionExecutor.java
    oozie/trunk/core/src/main/resources/ssh-base.sh
    oozie/trunk/core/src/main/resources/ssh-wrapper.sh
    oozie/trunk/core/src/test/java/org/apache/oozie/action/ssh/TestSshActionExecutor.java
    oozie/trunk/core/src/test/java/org/apache/oozie/action/ssh/TestSshActionExecutorAsExtension.java
    oozie/trunk/docs/src/site/twiki/DG_SshActionExtension.twiki
    oozie/trunk/release-log.txt

Modified: oozie/trunk/client/src/main/java/org/apache/oozie/cli/OozieCLI.java
URL: http://svn.apache.org/viewvc/oozie/trunk/client/src/main/java/org/apache/oozie/cli/OozieCLI.java?rev=1464683&r1=1464682&r2=1464683&view=diff
==============================================================================
--- oozie/trunk/client/src/main/java/org/apache/oozie/cli/OozieCLI.java (original)
+++ oozie/trunk/client/src/main/java/org/apache/oozie/cli/OozieCLI.java Thu Apr  4 19:11:20 2013
@@ -1531,6 +1531,8 @@ public class OozieCLI {
                         "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();

Added: oozie/trunk/client/src/main/resources/ssh-action-0.2.xsd
URL: http://svn.apache.org/viewvc/oozie/trunk/client/src/main/resources/ssh-action-0.2.xsd?rev=1464683&view=auto
==============================================================================
--- oozie/trunk/client/src/main/resources/ssh-action-0.2.xsd (added)
+++ oozie/trunk/client/src/main/resources/ssh-action-0.2.xsd Thu Apr  4 19:11:20 2013
@@ -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>

Modified: oozie/trunk/core/src/main/conf/oozie-site.xml
URL: http://svn.apache.org/viewvc/oozie/trunk/core/src/main/conf/oozie-site.xml?rev=1464683&r1=1464682&r2=1464683&view=diff
==============================================================================
--- oozie/trunk/core/src/main/conf/oozie-site.xml (original)
+++ oozie/trunk/core/src/main/conf/oozie-site.xml Thu Apr  4 19:11:20 2013
@@ -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>

Modified: oozie/trunk/core/src/main/java/org/apache/oozie/action/ssh/SshActionExecutor.java
URL: http://svn.apache.org/viewvc/oozie/trunk/core/src/main/java/org/apache/oozie/action/ssh/SshActionExecutor.java?rev=1464683&r1=1464682&r2=1464683&view=diff
==============================================================================
--- oozie/trunk/core/src/main/java/org/apache/oozie/action/ssh/SshActionExecutor.java (original)
+++ oozie/trunk/core/src/main/java/org/apache/oozie/action/ssh/SshActionExecutor.java Thu Apr  4 19:11:20 2013
@@ -22,8 +22,10 @@ import java.io.File;
 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 class SshActionExecutor extends A
             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 @@ public class SshActionExecutor extends A
      * @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();

Modified: oozie/trunk/core/src/main/resources/ssh-base.sh
URL: http://svn.apache.org/viewvc/oozie/trunk/core/src/main/resources/ssh-base.sh?rev=1464683&r1=1464682&r2=1464683&view=diff
==============================================================================
--- oozie/trunk/core/src/main/resources/ssh-base.sh (original)
+++ oozie/trunk/core/src/main/resources/ssh-base.sh Thu Apr  4 19:11:20 2013
@@ -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

Modified: oozie/trunk/core/src/main/resources/ssh-wrapper.sh
URL: http://svn.apache.org/viewvc/oozie/trunk/core/src/main/resources/ssh-wrapper.sh?rev=1464683&r1=1464682&r2=1464683&view=diff
==============================================================================
--- oozie/trunk/core/src/main/resources/ssh-wrapper.sh (original)
+++ oozie/trunk/core/src/main/resources/ssh-wrapper.sh Thu Apr  4 19:11:20 2013
@@ -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
 

Modified: oozie/trunk/core/src/test/java/org/apache/oozie/action/ssh/TestSshActionExecutor.java
URL: http://svn.apache.org/viewvc/oozie/trunk/core/src/test/java/org/apache/oozie/action/ssh/TestSshActionExecutor.java?rev=1464683&r1=1464682&r2=1464683&view=diff
==============================================================================
--- oozie/trunk/core/src/test/java/org/apache/oozie/action/ssh/TestSshActionExecutor.java (original)
+++ oozie/trunk/core/src/test/java/org/apache/oozie/action/ssh/TestSshActionExecutor.java Thu Apr  4 19:11:20 2013
@@ -20,7 +20,9 @@ package org.apache.oozie.action.ssh;
 import org.apache.oozie.service.CallbackService;
 
 import java.io.IOException;
+import java.io.OutputStreamWriter;
 import java.io.StringReader;
+import java.io.Writer;
 import java.net.URISyntaxException;
 import java.util.Properties;
 
@@ -47,6 +49,13 @@ public class TestSshActionExecutor exten
 
     private Services services;
 
+    private String ECHO_ARGS_SCRIPT = "count=1\n" +
+                                      "for var in \"$@\";\n" +
+                                      "do\n" +
+                                      "     echo \"prop$count=$var\"\n" +
+                                      "     count=$((count + 1))\n" +
+                                      "done";
+
     private class Context implements ActionExecutor.Context {
         private WorkflowActionBean action;
         private WorkflowJobBean workflow;
@@ -370,6 +379,163 @@ public class TestSshActionExecutor exten
         }
     }
 
+    public void testSpaceInArgs() throws Exception {
+        String baseDir = getTestCaseDir();
+        Path appPath = new Path(getNameNodeUri(), baseDir);
+
+        Path script = new Path(baseDir, "script.sh");
+        FileSystem fs = FileSystem.getLocal(createJobConf());
+        Writer w = new OutputStreamWriter(fs.create(script));
+        w.write(ECHO_ARGS_SCRIPT);
+        w.close();
+
+        XConfiguration protoConf = new XConfiguration();
+        protoConf.setStrings(WorkflowAppService.HADOOP_USER, getTestUser());
+
+        XConfiguration wfConf = new XConfiguration();
+        wfConf.set(OozieClient.APP_PATH, appPath.toString());
+
+        WorkflowJobBean workflow = new WorkflowJobBean();
+        workflow.setConf(wfConf.toXmlString());
+        workflow.setAppPath(wfConf.get(OozieClient.APP_PATH));
+        workflow.setProtoActionConf(protoConf.toXmlString());
+        workflow.setId(Services.get().get(UUIDService.class).generateId(ApplicationType.WORKFLOW));
+
+        final WorkflowActionBean action = new WorkflowActionBean();
+        action.setId("actionId");
+        action.setConf("<ssh xmlns='" + getActionXMLSchema() + "'>" +
+                       "<host>localhost</host>" +
+                       "<command>" + script.toString() + "</command>" +
+                       "<capture-output/>" +
+                       "<args>something</args>" +
+                       "<args>Hello World</args>" +
+                       "<args>\"Goodbye Planet\"</args>" +
+                       "<args>\'Greetings Globe\'</args>" +
+                       "</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() {
+            @Override
+            public boolean evaluate() throws Exception {
+                ssh.check(context, action);
+                return Status.DONE == action.getStatus();
+            }
+        });
+        ssh.end(context, action);
+        assertEquals(Status.OK, action.getStatus());
+        assertEquals("something", PropertiesUtils.stringToProperties(action.getData()).getProperty("prop1"));
+        assertEquals("Hello", PropertiesUtils.stringToProperties(action.getData()).getProperty("prop2"));
+        assertEquals("World", PropertiesUtils.stringToProperties(action.getData()).getProperty("prop3"));
+        assertEquals("Goodbye", PropertiesUtils.stringToProperties(action.getData()).getProperty("prop4"));
+        assertEquals("Planet", PropertiesUtils.stringToProperties(action.getData()).getProperty("prop5"));
+        assertEquals("Greetings", PropertiesUtils.stringToProperties(action.getData()).getProperty("prop6"));
+        assertEquals("Globe", PropertiesUtils.stringToProperties(action.getData()).getProperty("prop7"));
+        assertEquals(7, PropertiesUtils.stringToProperties(action.getData()).size());
+    }
+
+    public void testSpaceInArg() throws Exception {
+        String baseDir = getTestCaseDir();
+        Path appPath = new Path(getNameNodeUri(), baseDir);
+
+        Path script = new Path(baseDir, "script.sh");
+        FileSystem fs = FileSystem.getLocal(createJobConf());
+        Writer w = new OutputStreamWriter(fs.create(script));
+        w.write(ECHO_ARGS_SCRIPT);
+        w.close();
+
+        XConfiguration protoConf = new XConfiguration();
+        protoConf.setStrings(WorkflowAppService.HADOOP_USER, getTestUser());
+
+        XConfiguration wfConf = new XConfiguration();
+        wfConf.set(OozieClient.APP_PATH, appPath.toString());
+
+        WorkflowJobBean workflow = new WorkflowJobBean();
+        workflow.setConf(wfConf.toXmlString());
+        workflow.setAppPath(wfConf.get(OozieClient.APP_PATH));
+        workflow.setProtoActionConf(protoConf.toXmlString());
+        workflow.setId(Services.get().get(UUIDService.class).generateId(ApplicationType.WORKFLOW));
+
+        final WorkflowActionBean action = new WorkflowActionBean();
+        action.setId("actionId");
+        action.setConf("<ssh xmlns='" + getActionXMLSchema() + "'>" +
+                       "<host>localhost</host>" +
+                       "<command>" + script.toString() + "</command>" +
+                       "<capture-output/>" +
+                       "<arg>something</arg>" +
+                       "<arg>Hello World</arg>" +
+                       "<arg>\"Goodbye Planet\"</arg>" +
+                       "<arg>\'Greetings Globe\'</arg>" +
+                       "</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() {
+            @Override
+            public boolean evaluate() throws Exception {
+                ssh.check(context, action);
+                return Status.DONE == action.getStatus();
+            }
+        });
+        ssh.end(context, action);
+        assertEquals(Status.OK, action.getStatus());
+        assertEquals("something", PropertiesUtils.stringToProperties(action.getData()).getProperty("prop1"));
+        assertEquals("Hello World", PropertiesUtils.stringToProperties(action.getData()).getProperty("prop2"));
+        assertEquals("Goodbye Planet", PropertiesUtils.stringToProperties(action.getData()).getProperty("prop3"));
+        assertEquals("Greetings Globe", PropertiesUtils.stringToProperties(action.getData()).getProperty("prop4"));
+        assertEquals(4, PropertiesUtils.stringToProperties(action.getData()).size());
+    }
+
+    public void testNoArgsNorArg() throws Exception {
+        String baseDir = getTestCaseDir();
+        Path appPath = new Path(getNameNodeUri(), baseDir);
+
+        Path script = new Path(baseDir, "script.sh");
+        FileSystem fs = FileSystem.getLocal(createJobConf());
+        Writer w = new OutputStreamWriter(fs.create(script));
+        w.write("echo \"prop1=something\"");
+        w.close();
+
+        XConfiguration protoConf = new XConfiguration();
+        protoConf.setStrings(WorkflowAppService.HADOOP_USER, getTestUser());
+
+        XConfiguration wfConf = new XConfiguration();
+        wfConf.set(OozieClient.APP_PATH, appPath.toString());
+
+        WorkflowJobBean workflow = new WorkflowJobBean();
+        workflow.setConf(wfConf.toXmlString());
+        workflow.setAppPath(wfConf.get(OozieClient.APP_PATH));
+        workflow.setProtoActionConf(protoConf.toXmlString());
+        workflow.setId(Services.get().get(UUIDService.class).generateId(ApplicationType.WORKFLOW));
+
+        final WorkflowActionBean action = new WorkflowActionBean();
+        action.setId("actionId");
+        action.setConf("<ssh xmlns='" + getActionXMLSchema() + "'>" +
+                       "<host>localhost</host>" +
+                       "<command>" + script.toString() + "</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() {
+            @Override
+            public boolean evaluate() throws Exception {
+                ssh.check(context, action);
+                return Status.DONE == action.getStatus();
+            }
+        });
+        ssh.end(context, action);
+        assertEquals(Status.OK, action.getStatus());
+        assertEquals("something", PropertiesUtils.stringToProperties(action.getData()).getProperty("prop1"));
+    }
+
     @Override
     protected void tearDown() throws Exception {
         services.destroy();

Modified: oozie/trunk/core/src/test/java/org/apache/oozie/action/ssh/TestSshActionExecutorAsExtension.java
URL: http://svn.apache.org/viewvc/oozie/trunk/core/src/test/java/org/apache/oozie/action/ssh/TestSshActionExecutorAsExtension.java?rev=1464683&r1=1464682&r2=1464683&view=diff
==============================================================================
--- oozie/trunk/core/src/test/java/org/apache/oozie/action/ssh/TestSshActionExecutorAsExtension.java (original)
+++ oozie/trunk/core/src/test/java/org/apache/oozie/action/ssh/TestSshActionExecutorAsExtension.java Thu Apr  4 19:11:20 2013
@@ -17,36 +17,11 @@
  */
 package org.apache.oozie.action.ssh;
 
-import org.apache.hadoop.conf.Configuration;
-import org.apache.hadoop.fs.FileSystem;
-import org.apache.hadoop.fs.Path;
-import org.apache.oozie.WorkflowActionBean;
-import org.apache.oozie.WorkflowJobBean;
-import org.apache.oozie.action.ActionExecutor;
-import org.apache.oozie.action.ActionExecutorException;
-import org.apache.oozie.client.OozieClient;
-import org.apache.oozie.client.WorkflowAction.Status;
-import org.apache.oozie.client.WorkflowJob;
-import org.apache.oozie.service.CallbackService;
-import org.apache.oozie.service.Services;
-import org.apache.oozie.service.UUIDService;
-import org.apache.oozie.service.UUIDService.ApplicationType;
-import org.apache.oozie.service.WorkflowAppService;
-import org.apache.oozie.test.XFsTestCase;
-import org.apache.oozie.util.ELEvaluator;
-import org.apache.oozie.util.PropertiesUtils;
-import org.apache.oozie.util.XConfiguration;
-
-import java.io.IOException;
-import java.io.StringReader;
-import java.net.URISyntaxException;
-import java.util.Properties;
-
 public class TestSshActionExecutorAsExtension extends TestSshActionExecutor {
 
     @Override
     protected String getActionXMLSchema() {
-        return "uri:oozie:ssh-action:0.1";
+        return "uri:oozie:ssh-action:0.2";
     }
 
 }

Modified: oozie/trunk/docs/src/site/twiki/DG_SshActionExtension.twiki
URL: http://svn.apache.org/viewvc/oozie/trunk/docs/src/site/twiki/DG_SshActionExtension.twiki?rev=1464683&r1=1464682&r2=1464683&view=diff
==============================================================================
--- oozie/trunk/docs/src/site/twiki/DG_SshActionExtension.twiki (original)
+++ oozie/trunk/docs/src/site/twiki/DG_SshActionExtension.twiki Thu Apr  4 19:11:20 2013
@@ -53,7 +53,10 @@ tot =true=.
 The =command= element indicates the shell command to execute.
 
 The =args= element, if present, contains parameters to be passed to the shell command. If more than one =args= element
-is present they are concatenated in order.
+is present they are concatenated in order. When an =args= element contains a space, even when quoted, it will be considered as
+separate arguments (i.e. "Hello World" becomes "Hello" and "World").  Starting with ssh schema 0.2, you can use the =arg= element
+(note that this is different than the =args= element) to specify arguments that have a space in them (i.e. "Hello World" is
+preserved as "Hello World").  You can use either =args= elements, =arg= elements, or neither; but not both in the same action.
 
 If the =capture-output= element is present, it indicates Oozie to capture output of the STDOUT of the ssh command
 execution. The ssh command output must be in Java Properties file format and it must not exceed 2KB. From within the
@@ -92,6 +95,34 @@ The output of the command will be ignore
 
 ---+++ AE.A Appendix A, Ssh XML-Schema
 
+---++++ Ssh Action Schema Version 0.2
+
+<verbatim>
+<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>
+</verbatim>
+
+---++++ Ssh Action Schema Version 0.1
+
 <verbatim>
 <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
            xmlns:ssh="uri:oozie:ssh-action:0.1" elementFormDefault="qualified"

Modified: oozie/trunk/release-log.txt
URL: http://svn.apache.org/viewvc/oozie/trunk/release-log.txt?rev=1464683&r1=1464682&r2=1464683&view=diff
==============================================================================
--- oozie/trunk/release-log.txt (original)
+++ oozie/trunk/release-log.txt Thu Apr  4 19:11:20 2013
@@ -1,5 +1,6 @@
 -- Oozie 4.1.0 release (trunk - unreleased)
 
+OOZIE-1286 SSH Action does not properly handle arguments that have spaces (rkanter)
 OOZIE-1300 [Doc] Error in the the email action XML schema (harsh)
 OOZIE-1288 Improve docs around -D arguments support in the Oozie CLI's pig subcommand (harsh)
 OOZIE-1291 TestHadoopAccessorService.testGetMRDelegationTokenRenewer fails against Yarn (rkanter)