From 8a656871d5790040412c9e4bed24fde423a484ff Mon Sep 17 00:00:00 2001
From: Fathi Boudra <fathi.boudra@linaro.org>
Date: Fri, 06 Jun 2014 17:50:26 +0300
Subject: [PATCH] builders: add 'publish over ssh' support as a build step

'Publish over SSH' plugin is only supported in the publishers.
It can also be used as a build step during the build process.

Adjustments to publishers.base_publish_over() method:
 * fix the missing top delegate
 * re-order command, timeout and use-pty as in Jenkins

Change-Id: Id45ef1b84b88213d8673f34597571ca56e3ff986
Signed-off-by: Fathi Boudra <fathi.boudra@linaro.org>

https://review.openstack.org/#/c/98437/
---

---
 jenkins_jobs/modules/builders.py              |   30 +++++++++++++++
 jenkins_jobs/modules/publishers.py            |   50 +++++++++++++++-----------
 tests/builders/fixtures/publish-over-ssh.xml  |   36 ++++++++++++++++++
 tests/builders/fixtures/publish-over-ssh.yaml |    6 +++
 tests/publishers/fixtures/ssh001.xml          |    6 +--
 5 files changed, 104 insertions(+), 24 deletions(-)

--- a/jenkins_jobs/modules/builders.py
+++ b/jenkins_jobs/modules/builders.py
@@ -53,6 +53,7 @@ from jenkins_jobs.modules.helpers import
 from jenkins_jobs.modules.helpers import convert_mapping_to_xml
 from jenkins_jobs.modules.helpers import copyartifact_build_selector
 from jenkins_jobs.modules import hudson_model
+from jenkins_jobs.modules.publishers import ssh
 
 logger = logging.getLogger(__name__)
 
@@ -2005,6 +2006,35 @@ def critical_block_end(parser, xml_paren
     cbs.set('plugin', 'Exclusion')
 
 
+def publish_over_ssh(parser, xml_parent, data):
+    """yaml: publish-over-ssh
+    Send files or execute commands over SSH.
+    Requires the Jenkins :jenkins-wiki:`Publish over SSH Plugin
+    <Publish+Over+SSH+Plugin>`.
+
+    :arg str site: name of the ssh site
+    :arg str target: destination directory
+    :arg bool target-is-date-format: whether target is a date format. If true,
+        raw text should be quoted (default false)
+    :arg bool clean-remote: should the remote directory be deleted before
+        transferring files (default false)
+    :arg str source: source path specifier
+    :arg str command: a command to execute on the remote server (optional)
+    :arg int timeout: timeout in milliseconds for the Exec command (optional)
+    :arg bool use-pty: run the exec command in pseudo TTY (default false)
+    :arg str excludes: excluded file pattern (optional)
+    :arg str remove-prefix: prefix to remove from uploaded file paths
+        (optional)
+    :arg bool fail-on-error: fail the build if an error occurs (default false)
+
+    Example:
+
+    .. literalinclude:: /../../tests/builders/fixtures/publish-over-ssh.yaml
+       :language: yaml
+    """
+    ssh(parser, xml_parent, data)
+
+
 class Builders(jenkins_jobs.modules.base.Base):
     sequence = 60
 
--- a/jenkins_jobs/modules/publishers.py
+++ b/jenkins_jobs/modules/publishers.py
@@ -1659,18 +1659,18 @@ def ssh(parser, xml_parent, data):
        :language: yaml
     """
     console_prefix = 'SSH: '
-    plugin_tag = 'jenkins.plugins.publish__over__ssh.BapSshPublisherPlugin'
-    publisher_tag = 'jenkins.plugins.publish__over__ssh.BapSshPublisher'
-    transfer_tag = 'jenkins.plugins.publish__over__ssh.BapSshTransfer'
-    plugin_reference_tag = 'jenkins.plugins.publish_over_ssh.'    \
-        'BapSshPublisherPlugin'
-    base_publish_over(xml_parent,
-                      data,
-                      console_prefix,
-                      plugin_tag,
-                      publisher_tag,
-                      transfer_tag,
-                      plugin_reference_tag)
+    tag_prefix = 'jenkins.plugins.publish'
+    publisher_tag = '%s__over__ssh.BapSshPublisher' % tag_prefix
+    transfer_tag = '%s__over__ssh.BapSshTransfer' % tag_prefix
+    reference_tag = '%s_over_ssh.BapSshPublisherPlugin' % tag_prefix
+
+    if xml_parent.tag == 'publishers':
+        plugin_tag = '%s__over__ssh.BapSshPublisherPlugin' % tag_prefix
+    else:
+        plugin_tag = '%s__over__ssh.BapSshBuilderPlugin' % tag_prefix
+
+    base_publish_over(xml_parent, data, console_prefix, plugin_tag,
+                      publisher_tag, transfer_tag, reference_tag)
 
 
 def pipeline(parser, xml_parent, data):
@@ -2233,24 +2233,23 @@ def base_publish_over(xml_parent, data,
                       plugin_tag, publisher_tag,
                       transferset_tag, reference_plugin_tag):
     outer = XML.SubElement(xml_parent, plugin_tag)
+    # 'Publish over SSH' builder has an extra top delegate element
+    if xml_parent.tag == 'builders':
+        outer = XML.SubElement(outer, 'delegate')
+
     XML.SubElement(outer, 'consolePrefix').text = console_prefix
     delegate = XML.SubElement(outer, 'delegate')
     publishers = XML.SubElement(delegate, 'publishers')
+
     inner = XML.SubElement(publishers, publisher_tag)
     XML.SubElement(inner, 'configName').text = data['site']
     XML.SubElement(inner, 'verbose').text = 'true'
 
     transfers = XML.SubElement(inner, 'transfers')
     transfersset = XML.SubElement(transfers, transferset_tag)
+
     XML.SubElement(transfersset, 'remoteDirectory').text = data['target']
     XML.SubElement(transfersset, 'sourceFiles').text = data['source']
-    if 'command' in data:
-        XML.SubElement(transfersset, 'execCommand').text = data['command']
-    if 'timeout' in data:
-        XML.SubElement(transfersset, 'execTimeout').text = str(data['timeout'])
-    if 'use-pty' in data:
-        XML.SubElement(transfersset, 'usePty').text = \
-            str(data.get('use-pty', False)).lower()
     XML.SubElement(transfersset, 'excludes').text = data.get('excludes', '')
     XML.SubElement(transfersset, 'removePrefix').text = \
         data.get('remove-prefix', '')
@@ -2261,16 +2260,25 @@ def base_publish_over(xml_parent, data,
     XML.SubElement(transfersset, 'cleanRemote').text = \
         str(data.get('clean-remote', False)).lower()
 
+    if 'command' in data:
+        XML.SubElement(transfersset, 'execCommand').text = data['command']
+    if 'timeout' in data:
+        XML.SubElement(transfersset, 'execTimeout').text = str(data['timeout'])
+    if 'use-pty' in data:
+        XML.SubElement(transfersset, 'usePty').text = \
+            str(data.get('use-pty', False)).lower()
+
     XML.SubElement(inner, 'useWorkspaceInPromotion').text = 'false'
     XML.SubElement(inner, 'usePromotionTimestamp').text = 'false'
+
     XML.SubElement(delegate, 'continueOnError').text = 'false'
     XML.SubElement(delegate, 'failOnError').text = \
         str(data.get('fail-on-error', False)).lower()
     XML.SubElement(delegate, 'alwaysPublishFromMaster').text = \
         str(data.get('always-publish-from-master', False)).lower()
     XML.SubElement(delegate, 'hostConfigurationAccess',
-                   {'class': reference_plugin_tag,
-                    'reference': '../..'})
+                   {'class': reference_plugin_tag, 'reference': '../..'})
+
     return (outer, transfersset)
 
 
--- /dev/null
+++ b/tests/builders/fixtures/publish-over-ssh.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<project>
+  <builders>
+    <jenkins.plugins.publish__over__ssh.BapSshBuilderPlugin>
+      <delegate>
+        <consolePrefix>SSH: </consolePrefix>
+        <delegate>
+          <publishers>
+            <jenkins.plugins.publish__over__ssh.BapSshPublisher>
+              <configName>server.example.com</configName>
+              <verbose>true</verbose>
+              <transfers>
+                <jenkins.plugins.publish__over__ssh.BapSshTransfer>
+                  <remoteDirectory>dest/dir</remoteDirectory>
+                  <sourceFiles>base/source/dir/**</sourceFiles>
+                  <excludes/>
+                  <removePrefix/>
+                  <remoteDirectorySDF>false</remoteDirectorySDF>
+                  <flatten>false</flatten>
+                  <cleanRemote>false</cleanRemote>
+                  <execTimeout>1800000</execTimeout>
+                </jenkins.plugins.publish__over__ssh.BapSshTransfer>
+              </transfers>
+              <useWorkspaceInPromotion>false</useWorkspaceInPromotion>
+              <usePromotionTimestamp>false</usePromotionTimestamp>
+            </jenkins.plugins.publish__over__ssh.BapSshPublisher>
+          </publishers>
+          <continueOnError>false</continueOnError>
+          <failOnError>false</failOnError>
+          <alwaysPublishFromMaster>false</alwaysPublishFromMaster>
+          <hostConfigurationAccess class="jenkins.plugins.publish_over_ssh.BapSshPublisherPlugin" reference="../.."/>
+        </delegate>
+      </delegate>
+    </jenkins.plugins.publish__over__ssh.BapSshBuilderPlugin>
+  </builders>
+</project>
--- /dev/null
+++ b/tests/builders/fixtures/publish-over-ssh.yaml
@@ -0,0 +1,6 @@
+builders:
+    - publish-over-ssh:
+        site: 'server.example.com'
+        target: 'dest/dir'
+        source: 'base/source/dir/**'
+        timeout: 1800000
--- a/tests/publishers/fixtures/ssh001.xml
+++ b/tests/publishers/fixtures/ssh001.xml
@@ -12,14 +12,14 @@
               <jenkins.plugins.publish__over__ssh.BapSshTransfer>
                 <remoteDirectory>dest/dir</remoteDirectory>
                 <sourceFiles>base/source/dir/**</sourceFiles>
-                <execCommand>rm -r jenkins_$BUILD_NUMBER</execCommand>
-                <execTimeout>1800000</execTimeout>
-                <usePty>true</usePty>
                 <excludes>**/*.excludedfiletype</excludes>
                 <removePrefix>base/source/dir</removePrefix>
                 <remoteDirectorySDF>false</remoteDirectorySDF>
                 <flatten>true</flatten>
                 <cleanRemote>false</cleanRemote>
+                <execCommand>rm -r jenkins_$BUILD_NUMBER</execCommand>
+                <execTimeout>1800000</execTimeout>
+                <usePty>true</usePty>
               </jenkins.plugins.publish__over__ssh.BapSshTransfer>
             </transfers>
             <useWorkspaceInPromotion>false</useWorkspaceInPromotion>
