From f7fa4ce059a156d99b1db28315cf4dffa969aad8 Mon Sep 17 00:00:00 2001
From: Nickolas Pohilets <pohilets@gmail.com>
Date: Wed, 13 Sep 2017 17:25:05 +0200
Subject: [PATCH] Adds support for executing Groovy scripts on jenkins nodes

Change-Id: I4c9d266073c3ba2a85f2c58a44afc2f041bf37f1
Signed-off-by: Sorin Sbarnea <ssbarnea@redhat.com>
---
 jenkins/__init__.py  | 16 ++++++++++++----
 tests/test_script.py |  9 +++++++++
 2 files changed, 21 insertions(+), 4 deletions(-)

diff --git a/jenkins/__init__.py b/jenkins/__init__.py
index c014327..5cb99ea 100755
--- a/jenkins/__init__.py
+++ b/jenkins/__init__.py
@@ -135,6 +135,7 @@ CREATE_VIEW = '%(folder_url)screateView?name=%(short_name)s'
 CONFIG_VIEW = '%(folder_url)sview/%(short_name)s/config.xml'
 DELETE_VIEW = '%(folder_url)sview/%(short_name)s/doDelete'
 SCRIPT_TEXT = 'scriptText'
+NODE_SCRIPT_TEXT = 'computer/%(node)s/scriptText'
 PROMOTION_NAME = '%(folder_url)sjob/%(short_name)s/promotion/process/%(name)s/api/json?tree=name'
 PROMOTION_INFO = '%(folder_url)sjob/%(short_name)s/promotion/api/json?depth=%(depth)s'
 DELETE_PROMOTION = '%(folder_url)sjob/%(short_name)s/promotion/process/%(name)s/doDelete'
@@ -1281,10 +1282,12 @@ class Jenkins(object):
         number = int(parts[-1])
         return number
 
-    def run_script(self, script):
-        '''Execute a groovy script on the jenkins master.
+    def run_script(self, script, node=None):
+        '''Execute a groovy script on the jenkins master or on a node if
+        specified..
 
         :param script: The groovy script, ``string``
+        :param node: Node to run the script on, defaults to None (master).
         :returns: The result of the script run.
 
         Example::
@@ -1297,9 +1300,14 @@ class Jenkins(object):
         '''
         magic_str = ')]}.'
         print_magic_str = 'println()\nprint("{}")'.format(magic_str)
-        groovy = {'script': script.encode('utf-8') + print_magic_str.encode('utf-8')}
+        data = {'script': script.encode('utf-8') + print_magic_str.encode('utf-8')}
+        if node:
+            url = self._build_url(NODE_SCRIPT_TEXT, locals())
+        else:
+            url = self._build_url(SCRIPT_TEXT, locals())
+
         result = self.jenkins_open(requests.Request(
-            'POST', self._build_url(SCRIPT_TEXT), data=groovy))
+            'POST', url, data=data))
 
         if not result.endswith(magic_str):
             raise JenkinsException(result)
diff --git a/tests/test_script.py b/tests/test_script.py
index ce8e718..82eb6b1 100644
--- a/tests/test_script.py
+++ b/tests/test_script.py
@@ -15,6 +15,15 @@ class JenkinsScriptTest(JenkinsTestBase):
             self.make_url('scriptText'))
         self._check_requests(jenkins_mock.call_args_list)
 
+    @patch.object(jenkins.Jenkins, 'jenkins_open')
+    def test_run_script_node(self, jenkins_mock):
+        self.j.run_script(u'println(\"Hello World!\")', node='(master)')
+
+        self.assertEqual(
+            jenkins_mock.call_args[0][0].url,
+            self.make_url('computer/(master)/scriptText'))
+        self._check_requests(jenkins_mock.call_args_list)
+
     @patch.object(jenkins.Jenkins, 'jenkins_open')
     def test_run_script_urlproof(self, jenkins_mock):
         self.j.run_script(u'if (a == b && c ==d) { println(\"Yes\")}')