You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@libcloud.apache.org by to...@apache.org on 2013/01/28 05:15:45 UTC

svn commit: r1439248 - in /libcloud/branches/0.12.x: ./ CHANGES libcloud/compute/ssh.py libcloud/test/compute/test_ssh_client.py setup.py tox.ini

Author: tomaz
Date: Mon Jan 28 04:15:44 2013
New Revision: 1439248

URL: http://svn.apache.org/viewvc?rev=1439248&view=rev
Log:
Backport commits from r1439233:r1439246 from trunk:

Fix the ScriptDeployment step to work correctly if user provides a relative path
for the script argument.

Contributed by Jaume devesa, part of LIBCLOUD-278.

Modified:
    libcloud/branches/0.12.x/   (props changed)
    libcloud/branches/0.12.x/CHANGES
    libcloud/branches/0.12.x/libcloud/compute/ssh.py
    libcloud/branches/0.12.x/libcloud/test/compute/test_ssh_client.py
    libcloud/branches/0.12.x/setup.py
    libcloud/branches/0.12.x/tox.ini

Propchange: libcloud/branches/0.12.x/
------------------------------------------------------------------------------
  Merged /libcloud/trunk:r1439233-1439247

Modified: libcloud/branches/0.12.x/CHANGES
URL: http://svn.apache.org/viewvc/libcloud/branches/0.12.x/CHANGES?rev=1439248&r1=1439247&r2=1439248&view=diff
==============================================================================
--- libcloud/branches/0.12.x/CHANGES (original)
+++ libcloud/branches/0.12.x/CHANGES Mon Jan 28 04:15:44 2013
@@ -98,6 +98,13 @@ Changes with Apache Libcloud 0.12.0:
      'create_node' method. (LIBCLOUD-282)
      [Joe Miller, Tomaz Muraus]
 
+   - Improve error handling in the Brightbox driver.
+     [Tomaz Muraus]
+
+   - Fix the ScriptDeployment step to work correctly if user provides a
+     relative path for the script argument. (LIBCLOUD-278)
+     [Jaume Devesa]
+
   *) Storage
 
     - Add a new local storage driver.
@@ -127,9 +134,6 @@ Changes with Apache Libcloud 0.12.0:
       (LIBCLOUD-269)
       [Mahendra M]
 
-    - Improve error handling in the Brightbox driver.
-      [Tomaz Muraus]
-
   *) DNS
 
     - Update 'if type' checks in the update_record methods to behave correctly

Modified: libcloud/branches/0.12.x/libcloud/compute/ssh.py
URL: http://svn.apache.org/viewvc/libcloud/branches/0.12.x/libcloud/compute/ssh.py?rev=1439248&r1=1439247&r2=1439248&view=diff
==============================================================================
--- libcloud/branches/0.12.x/libcloud/compute/ssh.py (original)
+++ libcloud/branches/0.12.x/libcloud/compute/ssh.py Mon Jan 28 04:15:44 2013
@@ -29,6 +29,7 @@ except ImportError:
 # Ref: https://bugs.launchpad.net/paramiko/+bug/392973
 
 from os.path import split as psplit
+from os.path import join as pjoin
 
 
 class BaseSSHClient(object):
@@ -120,6 +121,7 @@ class BaseSSHClient(object):
 
 
 class ParamikoSSHClient(BaseSSHClient):
+
     """
     A SSH Client powered by Paramiko.
     """
@@ -179,6 +181,19 @@ class ParamikoSSHClient(BaseSSHClient):
         sftp.close()
 
     def run(self, cmd):
+        if cmd[0] != '/':
+            # If 'cmd' based on relative path,
+            # set the absoute path joining the HOME path
+            sftp = self.client.open_sftp()
+            # Chdir to its own directory is mandatory because otherwise
+            # the 'getcwd()' method returns None
+            sftp.chdir('.')
+            cwd = sftp.getcwd()
+            sftp.close()
+
+            # Join the command to the current path
+            cmd = pjoin(cwd, cmd)
+
         # based on exec_command()
         bufsize = -1
         t = self.client.get_transport()

Modified: libcloud/branches/0.12.x/libcloud/test/compute/test_ssh_client.py
URL: http://svn.apache.org/viewvc/libcloud/branches/0.12.x/libcloud/test/compute/test_ssh_client.py?rev=1439248&r1=1439247&r2=1439248&view=diff
==============================================================================
--- libcloud/branches/0.12.x/libcloud/test/compute/test_ssh_client.py (original)
+++ libcloud/branches/0.12.x/libcloud/test/compute/test_ssh_client.py Mon Jan 28 04:15:44 2013
@@ -17,13 +17,156 @@
 import sys
 import unittest
 
-import libcloud.compute.ssh
+from libcloud.compute.ssh import ParamikoSSHClient
+from libcloud.compute.ssh import have_paramiko
 
-from mock import Mock
+from mock import patch, Mock
+
+if not have_paramiko:
+    ParamikoSSHClient = None
 
 
 class ParamikoSSHClientTests(unittest.TestCase):
-    pass
+    @patch('paramiko.SSHClient', Mock)
+    def setUp(self):
+        """
+        Creates the object patching the actual connection.
+        """
+        conn_params = {'hostname': 'dummy.host.org',
+                       'port': 8822,
+                       'username': 'ubuntu',
+                       'key': '~/.ssh/ubuntu_ssh',
+                       'timeout': '600'}
+        self.ssh_cli = ParamikoSSHClient(**conn_params)
+
+    @patch('paramiko.SSHClient', Mock)
+    def test_create_with_password(self):
+        """
+        Initialize object with password.
+
+        Just to have better coverage, initialize the object
+        with the 'password' value instead of the 'key'.
+        """
+        conn_params = {'hostname': 'dummy.host.org',
+                       'username': 'ubuntu',
+                       'password': 'ubuntu'}
+        mock = ParamikoSSHClient(**conn_params)
+        mock.connect()
+
+        expected_conn = {'username': 'ubuntu',
+                         'password': 'ubuntu',
+                         'allow_agent': False,
+                         'hostname': 'dummy.host.org',
+                         'look_for_keys': False,
+                         'port': 22}
+        mock.client.connect.assert_called_once_with(**expected_conn)
+
+    @patch('paramiko.SSHClient', Mock)
+    def test_create_without_credentials(self):
+        """
+        Initialize object with no credentials.
+
+        Just to have better coverage, initialize the object
+        without 'password' neither 'key'.
+        """
+        conn_params = {'hostname': 'dummy.host.org',
+                       'username': 'ubuntu'}
+        mock = ParamikoSSHClient(**conn_params)
+        mock.connect()
+
+        expected_conn = {'username': 'ubuntu',
+                         'hostname': 'dummy.host.org',
+                         'allow_agent': True,
+                         'look_for_keys': True,
+                         'port': 22}
+        mock.client.connect.assert_called_once_with(**expected_conn)
+
+    def test_basic_usage_absolute_path(self):
+        """
+        Basic execution.
+        """
+        mock = self.ssh_cli
+        # script to execute
+        sd = "/root/random_script.sh"
+
+        # Connect behavior
+        mock.connect()
+        mock_cli = mock.client  # The actual mocked object: SSHClient
+        expected_conn = {'username': 'ubuntu',
+                         'key_filename': '~/.ssh/ubuntu_ssh',
+                         'allow_agent': False,
+                         'hostname': 'dummy.host.org',
+                         'look_for_keys': False,
+                         'timeout': '600',
+                         'port': 8822}
+        mock_cli.connect.assert_called_once_with(**expected_conn)
+
+        mock.put(sd)
+        # Make assertions over 'put' method
+        mock_cli.open_sftp().chdir.assert_called_with('root')
+        mock_cli.open_sftp().file.assert_called_once_with('random_script.sh',
+                                                          mode='w')
+
+        mock.run(sd)
+        # Make assertions over 'run' method
+        mock_cli.get_transport().open_session().exec_command \
+                .assert_called_once_with(sd)
+
+        mock.close()
+
+    def test_run_script_with_relative_path(self):
+        """
+        Execute script with relative path.
+        """
+        mock = self.ssh_cli
+
+        # Define behaviour then ask for 'current directory'
+        mock.client.open_sftp().getcwd.return_value = '/home/ubuntu/'
+
+        # Script without full path
+        sd = 'random_script.sh'
+
+        # Without assertions because they are the same than the previous
+        # 'test_basic_usage' method
+        mock.connect()
+
+        mock_cli = mock.client  # The actual mocked object: SSHClient
+
+        mock.put(sd, chmod=600)
+        # Make assertions over 'put' method
+        mock_cli.open_sftp().file.assert_called_once_with('random_script.sh',
+                                                          mode='w')
+        mock_cli.open_sftp().file().chmod.assert_called_once_with(600)
+
+        mock.run(sd)
+        # Make assertions over the 'run' method
+        mock_cli.open_sftp().chdir.assert_called_with(".")
+        mock_cli.open_sftp().getcwd.assert_called_once()
+        full_sd = '/home/ubuntu/random_script.sh'
+        mock_cli.get_transport().open_session().exec_command \
+                .assert_called_once_with(full_sd)
+
+        mock.close()
+
+    def test_delete_script(self):
+        """
+        Provide a basic test with 'delete' action.
+        """
+        mock = self.ssh_cli
+        # script to execute
+        sd = '/root/random_script.sh'
+
+        mock.connect()
+
+        mock.delete(sd)
+        # Make assertions over the 'delete' method
+        mock.client.open_sftp().unlink.assert_called_with(sd)
+
+        mock.close()
+
+if not ParamikoSSHClient:
+    class ParamikoSSHClientTests(unittest.TestCase):
+        pass
 
 
 if __name__ == '__main__':

Modified: libcloud/branches/0.12.x/setup.py
URL: http://svn.apache.org/viewvc/libcloud/branches/0.12.x/setup.py?rev=1439248&r1=1439247&r2=1439248&view=diff
==============================================================================
--- libcloud/branches/0.12.x/setup.py (original)
+++ libcloud/branches/0.12.x/setup.py Mon Jan 28 04:15:44 2013
@@ -51,6 +51,9 @@ if sys.version_info <= (2, 4):
           ', '.join(SUPPORTED_VERSIONS))
     sys.exit(1)
 
+# pre-2.6 will need the ssl PyPI package
+pre_python26 = (sys.version_info[0] == 2 and sys.version_info[1] < 6)
+
 
 def read_version_string():
     version = None
@@ -106,8 +109,6 @@ class TestCommand(Command):
             print("Please copy the new secret.py-dist file over otherwise" +
                   " tests might fail")
 
-        pre_python26 = (sys.version_info[0] == 2
-                        and sys.version_info[1] < 6)
         if pre_python26:
             missing = []
             # test for dependencies
@@ -130,6 +131,9 @@ class TestCommand(Command):
         testfiles = []
         for test_path in TEST_PATHS:
             for t in glob(pjoin(self._dir, test_path, 'test_*.py')):
+                if pre_python26 and 'test_ssh_client' in t:
+                    # TODO: Need to update mock library on buildslave
+                    continue
                 testfiles.append('.'.join(
                     [test_path.replace('/', '.'), splitext(basename(t))[0]]))
 
@@ -215,8 +219,6 @@ class CoverageCommand(Command):
         cov.save()
         cov.html_report()
 
-# pre-2.6 will need the ssl PyPI package
-pre_python26 = (sys.version_info[0] == 2 and sys.version_info[1] < 6)
 
 setup(
     name='apache-libcloud',

Modified: libcloud/branches/0.12.x/tox.ini
URL: http://svn.apache.org/viewvc/libcloud/branches/0.12.x/tox.ini?rev=1439248&r1=1439247&r2=1439248&view=diff
==============================================================================
--- libcloud/branches/0.12.x/tox.ini (original)
+++ libcloud/branches/0.12.x/tox.ini Mon Jan 28 04:15:44 2013
@@ -5,6 +5,7 @@ envlist = py25,py26,py27,pypy,py32,py33
 [testenv]
 deps = mock
        lockfile
+       paramiko
 commands = python setup.py test
 
 [testenv:py25]
@@ -12,6 +13,7 @@ deps = mock
        lockfile
        ssl
        simplejson
+       paramiko
 
 [testenv:py32]
 deps = mock