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 2020/04/04 20:45:14 UTC
[libcloud] 12/21: Allow user to pass "timeout" argument to
ScriptDeployment and ScriptFileDeployment class.
This is an automated email from the ASF dual-hosted git repository.
tomaz pushed a commit to branch 2.8.x
in repository https://gitbox.apache.org/repos/asf/libcloud.git
commit ed30a8e0f6f563c17cf94feb6c13c27c77e08560
Author: Tomaz Muraus <to...@tomaz.me>
AuthorDate: Wed Apr 1 13:08:01 2020 +0200
Allow user to pass "timeout" argument to ScriptDeployment and
ScriptFileDeployment class.
With this argument, user can specify optional command run timeout for
those deployment steps.
---
libcloud/compute/deployment.py | 33 +++++++++++++++++++++++++------
libcloud/compute/ssh.py | 6 +++---
libcloud/test/compute/test_deployment.py | 34 +++++++++++++++++++++++++-------
3 files changed, 57 insertions(+), 16 deletions(-)
diff --git a/libcloud/compute/deployment.py b/libcloud/compute/deployment.py
index 0328725..7850078 100644
--- a/libcloud/compute/deployment.py
+++ b/libcloud/compute/deployment.py
@@ -138,8 +138,14 @@ class ScriptDeployment(Deployment):
you are running a plan shell script.
"""
- def __init__(self, script, args=None, name=None, delete=False):
- # type: (str, Optional[List[str]], Optional[str], bool) -> None
+ def __init__(self,
+ script, # type: str
+ args=None, # type: Optional[List[str]]
+ name=None, # type: Optional[str]
+ delete=False, # type bool
+ timeout=None # type: Optional[float]
+ ):
+ # type: (...) -> None
"""
:type script: ``str``
:keyword script: Contents of the script to run.
@@ -154,6 +160,9 @@ class ScriptDeployment(Deployment):
:type delete: ``bool``
:keyword delete: Whether to delete the script on completion.
+
+ :param timeout: Optional run timeout for this command.
+ :type timeout: ``float``
"""
script = self._get_string_value(argument_name='script',
argument_value=script)
@@ -164,6 +173,7 @@ class ScriptDeployment(Deployment):
self.stderr = None # type: Optional[str]
self.exit_status = None # type: Optional[int]
self.delete = delete
+ self.timeout = timeout
self.name = name # type: Optional[str]
if self.name is None:
@@ -202,7 +212,8 @@ class ScriptDeployment(Deployment):
else:
cmd = name
- self.stdout, self.stderr, self.exit_status = client.run(cmd)
+ self.stdout, self.stderr, self.exit_status = \
+ client.run(cmd, timeout=self.timeout)
if self.delete:
client.delete(self.name)
@@ -234,8 +245,14 @@ class ScriptFileDeployment(ScriptDeployment):
the script content.
"""
- def __init__(self, script_file, args=None, name=None, delete=False):
- # type: (str, Optional[List[str]], Optional[str], bool) -> None
+ def __init__(self,
+ script_file, # type: str
+ args=None, # type: Optional[List[str]]
+ name=None, # type: Optional[str]
+ delete=False, # type bool
+ timeout=None # type: Optional[float]
+ ):
+ # type: (...) -> None
"""
:type script_file: ``str``
:keyword script_file: Path to a file containing the script to run.
@@ -251,6 +268,9 @@ class ScriptFileDeployment(ScriptDeployment):
:type delete: ``bool``
:keyword delete: Whether to delete the script on completion.
+
+ :param timeout: Optional run timeout for this command.
+ :type timeout: ``float``
"""
with open(script_file, 'rb') as fp:
content = fp.read() # type: Union[bytes, str]
@@ -262,7 +282,8 @@ class ScriptFileDeployment(ScriptDeployment):
super(ScriptFileDeployment, self).__init__(script=content,
args=args,
name=name,
- delete=delete)
+ delete=delete,
+ timeout=timeout)
class MultiStepDeployment(Deployment):
diff --git a/libcloud/compute/ssh.py b/libcloud/compute/ssh.py
index 887a4e1..0c65abf 100644
--- a/libcloud/compute/ssh.py
+++ b/libcloud/compute/ssh.py
@@ -178,8 +178,8 @@ class BaseSSHClient(object):
raise NotImplementedError(
'delete not implemented for this ssh client')
- def run(self, cmd):
- # type: (str) -> Tuple[str, str, int]
+ def run(self, cmd, timeout=None):
+ # type: (str, Optional[float]) -> Tuple[str, str, int]
"""
Run a command on a remote node.
@@ -616,7 +616,7 @@ class ShellOutSSHClient(BaseSSHClient):
"""
return True
- def run(self, cmd):
+ def run(self, cmd, timeout=None):
return self._run_remote_shell_command([cmd])
def put(self, path, contents=None, chmod=None, mode='w'):
diff --git a/libcloud/test/compute/test_deployment.py b/libcloud/test/compute/test_deployment.py
index 93d2d2f..5b96783 100644
--- a/libcloud/test/compute/test_deployment.py
+++ b/libcloud/test/compute/test_deployment.py
@@ -58,15 +58,19 @@ class MockDeployment(Deployment):
class MockClient(BaseSSHClient):
- def __init__(self, *args, **kwargs):
+ def __init__(self, throw_on_timeout=False, *args, **kwargs):
self.stdout = ''
self.stderr = ''
self.exit_status = 0
+ self.throw_on_timeout = throw_on_timeout
def put(self, path, contents, chmod=755, mode='w'):
return contents
- def run(self, name):
+ def run(self, cmd, timeout=None):
+ if self.throw_on_timeout and timeout is not None:
+ raise ValueError("timeout")
+
return self.stdout, self.stderr, self.exit_status
def delete(self, name):
@@ -118,14 +122,25 @@ class DeploymentTests(unittest.TestCase):
sd2 = ScriptDeployment(script='foobar', delete=False)
sd3 = ScriptDeployment(
script='foobar', delete=False, name='foobarname')
+ sd4 = ScriptDeployment(
+ script='foobar', delete=False, name='foobarname', timeout=10)
self.assertTrue(sd1.name.find('deployment') != '1')
self.assertEqual(sd3.name, 'foobarname')
+ self.assertEqual(sd3.timeout, None)
+ self.assertEqual(sd4.timeout, 10)
self.assertEqual(self.node, sd1.run(node=self.node,
client=MockClient(hostname='localhost')))
self.assertEqual(self.node, sd2.run(node=self.node,
client=MockClient(hostname='localhost')))
+ self.assertEqual(self.node, sd3.run(node=self.node,
+ client=MockClient(hostname='localhost')))
+
+ assertRaisesRegex(self, ValueError, 'timeout', sd4.run,
+ node=self.node,
+ client=MockClient(hostname='localhost',
+ throw_on_timeout=True))
def test_script_file_deployment(self):
file_path = os.path.abspath(__file__)
@@ -137,6 +152,10 @@ class DeploymentTests(unittest.TestCase):
sfd1 = ScriptFileDeployment(script_file=file_path)
self.assertEqual(sfd1.script, content)
+ self.assertEqual(sfd1.timeout, None)
+
+ sfd2 = ScriptFileDeployment(script_file=file_path, timeout=20)
+ self.assertEqual(sfd2.timeout, 20)
def test_script_deployment_relative_path(self):
client = Mock()
@@ -146,7 +165,7 @@ class DeploymentTests(unittest.TestCase):
sd = ScriptDeployment(script='echo "foo"', name='relative.sh')
sd.run(self.node, client)
- client.run.assert_called_once_with('/home/ubuntu/relative.sh')
+ client.run.assert_called_once_with('/home/ubuntu/relative.sh', timeout=None)
def test_script_deployment_absolute_path(self):
client = Mock()
@@ -156,7 +175,7 @@ class DeploymentTests(unittest.TestCase):
sd = ScriptDeployment(script='echo "foo"', name='/root/relative.sh')
sd.run(self.node, client)
- client.run.assert_called_once_with('/root/relative.sh')
+ client.run.assert_called_once_with('/root/relative.sh', timeout=None)
def test_script_deployment_with_arguments(self):
client = Mock()
@@ -169,7 +188,7 @@ class DeploymentTests(unittest.TestCase):
sd.run(self.node, client)
expected = '/root/relative.sh arg1 arg2 --option1=test'
- client.run.assert_called_once_with(expected)
+ client.run.assert_called_once_with(expected, timeout=None)
client.reset_mock()
@@ -179,7 +198,7 @@ class DeploymentTests(unittest.TestCase):
sd.run(self.node, client)
expected = '/root/relative.sh'
- client.run.assert_called_once_with(expected)
+ client.run.assert_called_once_with(expected, timeout=None)
def test_script_file_deployment_with_arguments(self):
file_path = os.path.abspath(__file__)
@@ -194,7 +213,7 @@ class DeploymentTests(unittest.TestCase):
sfd.run(self.node, client)
expected = '/root/relative.sh arg1 arg2 --option1=test option2'
- client.run.assert_called_once_with(expected)
+ client.run.assert_called_once_with(expected, timeout=None)
def test_script_deployment_and_sshkey_deployment_argument_types(self):
class FileObject(object):
@@ -479,6 +498,7 @@ class DeploymentTests(unittest.TestCase):
# the arguments
global call_count
call_count = 0
+
def create_node(name, image, size, ex_custom_arg_1, ex_custom_arg_2,
ex_foo=None, auth=None, **kwargs):
global call_count