class DeploymentTests(unittest.TestCase): def setUp(self): Rackspace.connectionCls.conn_class = RackspaceMockHttp RackspaceMockHttp.type = None self.driver = Rackspace(*RACKSPACE_PARAMS) # normally authentication happens lazily, but we force it here self.driver.connection._populate_hosts_and_request_paths() self.driver.features = {'create_node': ['generates_password']} self.node = Node(id=12345, name='test', state=NodeState.RUNNING, public_ips=['1.2.3.4'], private_ips=['1.2.3.5'], driver=Rackspace) self.node2 = Node(id=123456, name='test', state=NodeState.RUNNING, public_ips=['1.2.3.4'], private_ips=['1.2.3.5'], driver=Rackspace) def test_multi_step_deployment(self): msd = MultiStepDeployment() self.assertEqual(len(msd.steps), 0) msd.add(MockDeployment()) self.assertEqual(len(msd.steps), 1) self.assertEqual(self.node, msd.run(node=self.node, client=None)) def test_ssh_key_deployment(self): sshd = SSHKeyDeployment(key='1234') self.assertEqual( self.node, sshd.run(node=self.node, client=MockClient(hostname='localhost'))) def test_file_deployment(self): # use this file (__file__) for obtaining permissions target = os.path.join('/tmp', os.path.basename(__file__)) fd = FileDeployment(__file__, target) self.assertEqual(target, fd.target) self.assertEqual(__file__, fd.source) self.assertEqual( self.node, fd.run(node=self.node, client=MockClient(hostname='localhost'))) def test_script_deployment(self): sd1 = ScriptDeployment(script='foobar', delete=True) sd2 = ScriptDeployment(script='foobar', delete=False) sd3 = ScriptDeployment(script='foobar', delete=False, name='foobarname') self.assertTrue(sd1.name.find('deployment') != '1') self.assertEqual(sd3.name, 'foobarname') 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'))) def test_script_file_deployment(self): file_path = os.path.abspath(__file__) with open(file_path, 'rb') as fp: content = fp.read() if PY3: content = content.decode('utf-8') sfd1 = ScriptFileDeployment(script_file=file_path) self.assertEqual(sfd1.script, content) def test_script_deployment_relative_path(self): client = Mock() client.put.return_value = '/home/ubuntu/relative.sh' client.run.return_value = ('', '', 0) sd = ScriptDeployment(script='echo "foo"', name='relative.sh') sd.run(self.node, client) client.run.assert_called_once_with('/home/ubuntu/relative.sh') def test_script_deployment_absolute_path(self): client = Mock() client.put.return_value = '/home/ubuntu/relative.sh' client.run.return_value = ('', '', 0) sd = ScriptDeployment(script='echo "foo"', name='/root/relative.sh') sd.run(self.node, client) client.run.assert_called_once_with('/root/relative.sh') def test_script_deployment_with_arguments(self): client = Mock() client.put.return_value = '/home/ubuntu/relative.sh' client.run.return_value = ('', '', 0) args = ['arg1', 'arg2', '--option1=test'] sd = ScriptDeployment(script='echo "foo"', args=args, name='/root/relative.sh') sd.run(self.node, client) expected = '/root/relative.sh arg1 arg2 --option1=test' client.run.assert_called_once_with(expected) client.reset_mock() args = [] sd = ScriptDeployment(script='echo "foo"', args=args, name='/root/relative.sh') sd.run(self.node, client) expected = '/root/relative.sh' client.run.assert_called_once_with(expected) def test_script_file_deployment_with_arguments(self): file_path = os.path.abspath(__file__) client = Mock() client.put.return_value = '/home/ubuntu/relative.sh' client.run.return_value = ('', '', 0) args = ['arg1', 'arg2', '--option1=test', 'option2'] sfd = ScriptFileDeployment(script_file=file_path, args=args, name='/root/relative.sh') sfd.run(self.node, client) expected = '/root/relative.sh arg1 arg2 --option1=test option2' client.run.assert_called_once_with(expected) def test_script_deployment_and_sshkey_deployment_argument_types(self): class FileObject(object): def __init__(self, name): self.name = name def read(self): return 'bar' ScriptDeployment(script='foobar') ScriptDeployment(script=u('foobar')) ScriptDeployment(script=FileObject('test')) SSHKeyDeployment(key='foobar') SSHKeyDeployment(key=u('foobar')) SSHKeyDeployment(key=FileObject('test')) try: ScriptDeployment(script=[]) except TypeError: pass else: self.fail('TypeError was not thrown') try: SSHKeyDeployment(key={}) except TypeError: pass else: self.fail('TypeError was not thrown') def test_wait_until_running_running_instantly(self): node2, ips = self.driver.wait_until_running(nodes=[self.node], wait_period=0.1, timeout=0.5)[0] self.assertEqual(self.node.uuid, node2.uuid) self.assertEqual(['67.23.21.33'], ips) def test_wait_until_running_without_ip(self): RackspaceMockHttp.type = 'NO_IP' try: node2, ips = self.driver.wait_until_running(nodes=[self.node], wait_period=0.1, timeout=0.2)[0] except LibcloudError as e: self.assertTrue(e.value.find('Timed out after 0.2 second') != -1) else: self.fail('Exception was not thrown') def test_wait_until_running_with_only_ipv6(self): RackspaceMockHttp.type = 'IPV6' try: node2, ips = self.driver.wait_until_running(nodes=[self.node], wait_period=0.1, timeout=0.2)[0] except LibcloudError as e: self.assertTrue(e.value.find('Timed out after 0.2 second') != -1) else: self.fail('Exception was not thrown') def test_wait_until_running_with_ipv6_ok(self): RackspaceMockHttp.type = 'IPV6' node2, ips = self.driver.wait_until_running(nodes=[self.node], wait_period=1, force_ipv4=False, timeout=0.5)[0] self.assertEqual(self.node.uuid, node2.uuid) self.assertEqual(['2001:DB8::1'], ips) def test_wait_until_running_running_after_0_2_second(self): RackspaceMockHttp.type = '05_SECOND_DELAY' node2, ips = self.driver.wait_until_running(nodes=[self.node], wait_period=0.2, timeout=0.1)[0] self.assertEqual(self.node.uuid, node2.uuid) self.assertEqual(['67.23.21.33'], ips) def test_wait_until_running_running_after_0_2_second_private_ips(self): RackspaceMockHttp.type = '05_SECOND_DELAY' node2, ips = self.driver.wait_until_running( nodes=[self.node], wait_period=0.2, timeout=0.1, ssh_interface='private_ips')[0] self.assertEqual(self.node.uuid, node2.uuid) self.assertEqual(['10.176.168.218'], ips) def test_wait_until_running_invalid_ssh_interface_argument(self): try: self.driver.wait_until_running(nodes=[self.node], wait_period=1, ssh_interface='invalid') except ValueError: pass else: self.fail('Exception was not thrown') def test_wait_until_running_timeout(self): RackspaceMockHttp.type = 'TIMEOUT' try: self.driver.wait_until_running(nodes=[self.node], wait_period=0.1, timeout=0.2) except LibcloudError as e: self.assertTrue(e.value.find('Timed out') != -1) else: self.fail('Exception was not thrown') def test_wait_until_running_running_node_missing_from_list_nodes(self): RackspaceMockHttp.type = 'MISSING' try: self.driver.wait_until_running(nodes=[self.node], wait_period=0.1, timeout=0.2) except LibcloudError as e: self.assertTrue(e.value.find('Timed out after 0.2 second') != -1) else: self.fail('Exception was not thrown') def test_wait_until_running_running_multiple_nodes_have_same_uuid(self): RackspaceMockHttp.type = 'SAME_UUID' try: self.driver.wait_until_running(nodes=[self.node], wait_period=0.1, timeout=0.2) except LibcloudError as e: self.assertTrue( e.value.find('Unable to match specified uuids') != -1) else: self.fail('Exception was not thrown') def test_wait_until_running_running_wait_for_multiple_nodes(self): RackspaceMockHttp.type = 'MULTIPLE_NODES' nodes = self.driver.wait_until_running(nodes=[self.node, self.node2], wait_period=0.1, timeout=0.5) self.assertEqual(self.node.uuid, nodes[0][0].uuid) self.assertEqual(self.node2.uuid, nodes[1][0].uuid) self.assertEqual(['67.23.21.33'], nodes[0][1]) self.assertEqual(['67.23.21.34'], nodes[1][1]) def test_ssh_client_connect_success(self): mock_ssh_client = Mock() mock_ssh_client.return_value = None ssh_client = self.driver._ssh_client_connect( ssh_client=mock_ssh_client, timeout=0.5) self.assertEqual(mock_ssh_client, ssh_client) def test_ssh_client_connect_timeout(self): mock_ssh_client = Mock() mock_ssh_client.connect = Mock() mock_ssh_client.connect.side_effect = IOError('bam') try: self.driver._ssh_client_connect(ssh_client=mock_ssh_client, wait_period=0.1, timeout=0.2) except LibcloudError as e: self.assertTrue(e.value.find('Giving up') != -1) else: self.fail('Exception was not thrown') def test_run_deployment_script_success(self): task = Mock() ssh_client = Mock() ssh_client2 = self.driver._run_deployment_script(task=task, node=self.node, ssh_client=ssh_client, max_tries=2) self.assertTrue(isinstance(ssh_client2, Mock)) def test_run_deployment_script_exception(self): task = Mock() task.run = Mock() task.run.side_effect = Exception('bar') ssh_client = Mock() try: self.driver._run_deployment_script(task=task, node=self.node, ssh_client=ssh_client, max_tries=2) except LibcloudError as e: self.assertTrue(e.value.find('Failed after 2 tries') != -1) else: self.fail('Exception was not thrown') @patch('libcloud.compute.base.SSHClient') @patch('libcloud.compute.ssh') def test_deploy_node_success(self, mock_ssh_module, _): self.driver.create_node = Mock() self.driver.create_node.return_value = self.node mock_ssh_module.have_paramiko = True deploy = Mock() node = self.driver.deploy_node(deploy=deploy) self.assertEqual(self.node.id, node.id) @patch('libcloud.compute.base.SSHClient') @patch('libcloud.compute.ssh') def test_deploy_node_deploy_node_kwargs_except_auth_are_not_propagated_on( self, mock_ssh_module, _): # Verify that keyword arguments which are specific to deploy_node() # are not propagated to create_node() mock_ssh_module.have_paramiko = True self.driver.create_node = Mock() self.driver.create_node.return_value = self.node self.driver._connect_and_run_deployment_script = Mock() self.driver._wait_until_running = Mock() kwargs = {} for key in DEPLOY_NODE_KWARGS: kwargs[key] = key kwargs['ssh_interface'] = 'public_ips' kwargs['ssh_alternate_usernames'] = ['foo', 'bar'] kwargs['timeout'] = 10 auth = NodeAuthPassword('P@$$w0rd') node = self.driver.deploy_node(name='name', image='image', size='size', auth=auth, ex_foo='ex_foo', **kwargs) self.assertEqual(self.node.id, node.id) self.assertEqual(self.driver.create_node.call_count, 1) call_kwargs = self.driver.create_node.call_args_list[0][1] expected_call_kwargs = { 'name': 'name', 'image': 'image', 'size': 'size', 'auth': auth, 'ex_foo': 'ex_foo' } self.assertEqual(expected_call_kwargs, call_kwargs) # If driver throws an exception it should fall back to passing in all # 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 call_count += 1 if call_count == 1: msg = 'create_node() takes at least 5 arguments (7 given)' raise TypeError(msg) return self.node self.driver.create_node = create_node node = self.driver.deploy_node(name='name', image='image', size='size', auth=auth, ex_foo='ex_foo', ex_custom_arg_1='a', ex_custom_arg_2='b', **kwargs) self.assertEqual(self.node.id, node.id) self.assertEqual(call_count, 2) 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 call_count += 1 if call_count == 1: msg = 'create_node() missing 3 required positional arguments' raise TypeError(msg) return self.node self.driver.create_node = create_node node = self.driver.deploy_node(name='name', image='image', size='size', auth=auth, ex_foo='ex_foo', ex_custom_arg_1='a', ex_custom_arg_2='b', **kwargs) self.assertEqual(self.node.id, node.id) self.assertEqual(call_count, 2) @patch('libcloud.compute.base.SSHClient') @patch('libcloud.compute.ssh') def test_deploy_node_exception_run_deployment_script( self, mock_ssh_module, _): self.driver.create_node = Mock() self.driver.create_node.return_value = self.node mock_ssh_module.have_paramiko = True deploy = Mock() deploy.run = Mock() deploy.run.side_effect = Exception('foo') try: self.driver.deploy_node(deploy=deploy) except DeploymentError as e: self.assertTrue(e.node.id, self.node.id) else: self.fail('Exception was not thrown') @patch('libcloud.compute.base.SSHClient') @patch('libcloud.compute.ssh') def test_deploy_node_exception_ssh_client_connect(self, mock_ssh_module, ssh_client): self.driver.create_node = Mock() self.driver.create_node.return_value = self.node mock_ssh_module.have_paramiko = True deploy = Mock() ssh_client.side_effect = IOError('bar') try: self.driver.deploy_node(deploy=deploy) except DeploymentError as e: self.assertTrue(e.node.id, self.node.id) else: self.fail('Exception was not thrown') @patch('libcloud.compute.ssh') def test_deploy_node_depoy_node_not_implemented(self, mock_ssh_module): self.driver.features = {'create_node': []} mock_ssh_module.have_paramiko = True try: self.driver.deploy_node(deploy=Mock()) except NotImplementedError: pass else: self.fail('Exception was not thrown') self.driver.features = {} try: self.driver.deploy_node(deploy=Mock()) except NotImplementedError: pass else: self.fail('Exception was not thrown') @patch('libcloud.compute.base.SSHClient') @patch('libcloud.compute.ssh') def test_deploy_node_password_auth(self, mock_ssh_module, _): self.driver.features = {'create_node': ['password']} mock_ssh_module.have_paramiko = True self.driver.create_node = Mock() self.driver.create_node.return_value = self.node node = self.driver.deploy_node(deploy=Mock()) self.assertEqual(self.node.id, node.id) @patch('libcloud.compute.base.SSHClient') @patch('libcloud.compute.ssh') def test_exception_is_thrown_is_paramiko_is_not_available( self, mock_ssh_module, _): self.driver.features = {'create_node': ['password']} self.driver.create_node = Mock() self.driver.create_node.return_value = self.node mock_ssh_module.have_paramiko = False try: self.driver.deploy_node(deploy=Mock()) except RuntimeError as e: self.assertTrue(str(e).find('paramiko is not installed') != -1) else: self.fail('Exception was not thrown') mock_ssh_module.have_paramiko = True node = self.driver.deploy_node(deploy=Mock()) self.assertEqual(self.node.id, node.id)
class DeploymentTests(unittest.TestCase): def setUp(self): Rackspace.connectionCls.conn_class = RackspaceMockHttp RackspaceMockHttp.type = None self.driver = Rackspace(*RACKSPACE_PARAMS) # normally authentication happens lazily, but we force it here self.driver.connection._populate_hosts_and_request_paths() self.driver.features = {'create_node': ['generates_password']} self.node = Node(id=12345, name='test', state=NodeState.RUNNING, public_ips=['1.2.3.4'], private_ips=['1.2.3.5'], driver=Rackspace) self.node2 = Node(id=123456, name='test', state=NodeState.RUNNING, public_ips=['1.2.3.4'], private_ips=['1.2.3.5'], driver=Rackspace) def test_multi_step_deployment(self): msd = MultiStepDeployment() self.assertEqual(len(msd.steps), 0) msd.add(MockDeployment()) self.assertEqual(len(msd.steps), 1) self.assertEqual(self.node, msd.run(node=self.node, client=None)) def test_ssh_key_deployment(self): sshd = SSHKeyDeployment(key='1234') self.assertEqual( self.node, sshd.run(node=self.node, client=MockClient(hostname='localhost'))) def test_file_deployment(self): # use this file (__file__) for obtaining permissions target = os.path.join('/tmp', os.path.basename(__file__)) fd = FileDeployment(__file__, target) self.assertEqual(target, fd.target) self.assertEqual(__file__, fd.source) self.assertEqual( self.node, fd.run(node=self.node, client=MockClient(hostname='localhost'))) def test_script_deployment(self): sd1 = ScriptDeployment(script='foobar', delete=True) 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__) with open(file_path, 'rb') as fp: content = fp.read() if PY3: content = content.decode('utf-8') 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() client.put.return_value = FILE_PATH client.run.return_value = ('', '', 0) sd = ScriptDeployment(script='echo "foo"', name='relative.sh') sd.run(self.node, client) client.run.assert_called_once_with(FILE_PATH, timeout=None) def test_script_deployment_absolute_path(self): file_path = '{0}root{0}relative.sh'.format(os.path.sep) client = Mock() client.put.return_value = file_path client.run.return_value = ('', '', 0) sd = ScriptDeployment(script='echo "foo"', name=file_path) sd.run(self.node, client) client.run.assert_called_once_with(file_path, timeout=None) def test_script_deployment_with_arguments(self): file_path = '{0}root{0}relative.sh'.format(os.path.sep) client = Mock() client.put.return_value = file_path client.run.return_value = ('', '', 0) args = ['arg1', 'arg2', '--option1=test'] sd = ScriptDeployment(script='echo "foo"', args=args, name=file_path) sd.run(self.node, client) expected = '%s arg1 arg2 --option1=test' % (file_path) client.run.assert_called_once_with(expected, timeout=None) client.reset_mock() args = [] sd = ScriptDeployment(script='echo "foo"', args=args, name=file_path) sd.run(self.node, client) expected = file_path client.run.assert_called_once_with(expected, timeout=None) def test_script_file_deployment_with_arguments(self): file_path = os.path.abspath(__file__) client = Mock() client.put.return_value = FILE_PATH client.run.return_value = ('', '', 0) args = ['arg1', 'arg2', '--option1=test', 'option2'] sfd = ScriptFileDeployment(script_file=file_path, args=args, name=file_path) sfd.run(self.node, client) expected = '%s arg1 arg2 --option1=test option2' % (file_path) client.run.assert_called_once_with(expected, timeout=None) def test_script_deployment_and_sshkey_deployment_argument_types(self): class FileObject(object): def __init__(self, name): self.name = name def read(self): return 'bar' ScriptDeployment(script='foobar') ScriptDeployment(script=u('foobar')) ScriptDeployment(script=FileObject('test')) SSHKeyDeployment(key='foobar') SSHKeyDeployment(key=u('foobar')) SSHKeyDeployment(key=FileObject('test')) try: ScriptDeployment(script=[]) except TypeError: pass else: self.fail('TypeError was not thrown') try: SSHKeyDeployment(key={}) except TypeError: pass else: self.fail('TypeError was not thrown') def test_wait_until_running_running_instantly(self): node2, ips = self.driver.wait_until_running(nodes=[self.node], wait_period=0.1, timeout=0.5)[0] self.assertEqual(self.node.uuid, node2.uuid) self.assertEqual(['67.23.21.33'], ips) def test_wait_until_running_without_ip(self): RackspaceMockHttp.type = 'NO_IP' try: node2, ips = self.driver.wait_until_running(nodes=[self.node], wait_period=0.1, timeout=0.2)[0] except LibcloudError as e: self.assertTrue(e.value.find('Timed out after 0.2 second') != -1) else: self.fail('Exception was not thrown') def test_wait_until_running_with_only_ipv6(self): RackspaceMockHttp.type = 'IPV6' try: node2, ips = self.driver.wait_until_running(nodes=[self.node], wait_period=0.1, timeout=0.2)[0] except LibcloudError as e: self.assertTrue(e.value.find('Timed out after 0.2 second') != -1) else: self.fail('Exception was not thrown') def test_wait_until_running_with_ipv6_ok(self): RackspaceMockHttp.type = 'IPV6' node2, ips = self.driver.wait_until_running(nodes=[self.node], wait_period=1, force_ipv4=False, timeout=0.5)[0] self.assertEqual(self.node.uuid, node2.uuid) self.assertEqual(['2001:DB8::1'], ips) def test_wait_until_running_running_after_0_2_second(self): RackspaceMockHttp.type = '05_SECOND_DELAY' node2, ips = self.driver.wait_until_running(nodes=[self.node], wait_period=0.2, timeout=0.1)[0] self.assertEqual(self.node.uuid, node2.uuid) self.assertEqual(['67.23.21.33'], ips) def test_wait_until_running_running_after_0_2_second_private_ips(self): RackspaceMockHttp.type = '05_SECOND_DELAY' node2, ips = self.driver.wait_until_running( nodes=[self.node], wait_period=0.2, timeout=0.1, ssh_interface='private_ips')[0] self.assertEqual(self.node.uuid, node2.uuid) self.assertEqual(['10.176.168.218'], ips) def test_wait_until_running_invalid_ssh_interface_argument(self): try: self.driver.wait_until_running(nodes=[self.node], wait_period=1, ssh_interface='invalid') except ValueError: pass else: self.fail('Exception was not thrown') def test_wait_until_running_timeout(self): RackspaceMockHttp.type = 'TIMEOUT' try: self.driver.wait_until_running(nodes=[self.node], wait_period=0.1, timeout=0.2) except LibcloudError as e: self.assertTrue(e.value.find('Timed out') != -1) else: self.fail('Exception was not thrown') def test_wait_until_running_running_node_missing_from_list_nodes(self): RackspaceMockHttp.type = 'MISSING' try: self.driver.wait_until_running(nodes=[self.node], wait_period=0.1, timeout=0.2) except LibcloudError as e: self.assertTrue(e.value.find('Timed out after 0.2 second') != -1) else: self.fail('Exception was not thrown') def test_wait_until_running_running_multiple_nodes_have_same_uuid(self): RackspaceMockHttp.type = 'SAME_UUID' try: self.driver.wait_until_running(nodes=[self.node], wait_period=0.1, timeout=0.2) except LibcloudError as e: self.assertTrue( e.value.find('Unable to match specified uuids') != -1) else: self.fail('Exception was not thrown') def test_wait_until_running_running_wait_for_multiple_nodes(self): RackspaceMockHttp.type = 'MULTIPLE_NODES' nodes = self.driver.wait_until_running(nodes=[self.node, self.node2], wait_period=0.1, timeout=0.5) self.assertEqual(self.node.uuid, nodes[0][0].uuid) self.assertEqual(self.node2.uuid, nodes[1][0].uuid) self.assertEqual(['67.23.21.33'], nodes[0][1]) self.assertEqual(['67.23.21.34'], nodes[1][1]) def test_ssh_client_connect_success(self): mock_ssh_client = Mock() mock_ssh_client.return_value = None ssh_client = self.driver._ssh_client_connect( ssh_client=mock_ssh_client, timeout=0.5) self.assertEqual(mock_ssh_client, ssh_client) def test_ssh_client_connect_timeout(self): mock_ssh_client = Mock() mock_ssh_client.connect = Mock() mock_ssh_client.connect.side_effect = IOError('bam') try: self.driver._ssh_client_connect(ssh_client=mock_ssh_client, wait_period=0.1, timeout=0.2) except LibcloudError as e: self.assertTrue(e.value.find('Giving up') != -1) else: self.fail('Exception was not thrown') @unittest.skipIf(not have_paramiko, 'Skipping because paramiko is not available') def test_ssh_client_connect_immediately_throws_on_fatal_execption(self): # Verify that fatal exceptions are immediately propagated and ensure # we don't try to retry on them from paramiko.ssh_exception import SSHException from paramiko.ssh_exception import PasswordRequiredException mock_ssh_client = Mock() mock_ssh_client.connect = Mock() mock_ssh_client.connect.side_effect = IOError('bam') mock_exceptions = [ SSHException('Invalid or unsupported key type'), PasswordRequiredException('private key file is encrypted'), SSHException('OpenSSH private key file checkints do not match') ] for mock_exception in mock_exceptions: mock_ssh_client.connect = Mock(side_effect=mock_exception) assertRaisesRegex(self, mock_exception.__class__, str(mock_exception), self.driver._ssh_client_connect, ssh_client=mock_ssh_client, wait_period=0.1, timeout=0.2) def test_run_deployment_script_success(self): task = Mock() ssh_client = Mock() ssh_client2 = self.driver._run_deployment_script(task=task, node=self.node, ssh_client=ssh_client, max_tries=2) self.assertTrue(isinstance(ssh_client2, Mock)) def test_run_deployment_script_exception(self): task = Mock() task.run = Mock() task.run.side_effect = Exception('bar') ssh_client = Mock() try: self.driver._run_deployment_script(task=task, node=self.node, ssh_client=ssh_client, max_tries=2) except LibcloudError as e: self.assertTrue(e.value.find('Failed after 2 tries') != -1) else: self.fail('Exception was not thrown') def test_run_deployment_script_ssh_command_timeout_fatal_exception(self): # We shouldn't retry on SSHCommandTimeoutError error since it's fatal task = Mock() task.run = Mock() task.run.side_effect = SSHCommandTimeoutError('ls -la', 10) ssh_client = Mock() try: self.driver._run_deployment_script(task=task, node=self.node, ssh_client=ssh_client, max_tries=5) except SSHCommandTimeoutError as e: self.assertTrue(e.message.find('Command didn\'t finish') != -1) else: self.fail('Exception was not thrown') def test_run_deployment_script_reconnect_on_ssh_session_not_active(self): # Verify that we try to reconnect if task.run() throws exception with # "SSH client not active" message global exception_counter exception_counter = 0 def mock_run(*args, **kwargs): # Mock run() method which throws "SSH session not active" exception # during first two calls and on third one it returns None. global exception_counter exception_counter += 1 if exception_counter > 2: return None raise Exception("SSH session not active") task = Mock() task.run = Mock() task.run = mock_run ssh_client = Mock() ssh_client.timeout = 20 self.assertEqual(ssh_client.connect.call_count, 0) self.assertEqual(ssh_client.close.call_count, 0) self.driver._run_deployment_script(task=task, node=self.node, ssh_client=ssh_client, max_tries=5) self.assertEqual(ssh_client.connect.call_count, 2) self.assertEqual(ssh_client.close.call_count, 2 + 1) @patch('libcloud.compute.base.SSHClient') @patch('libcloud.compute.ssh') def test_deploy_node_success(self, mock_ssh_module, _): self.driver.create_node = Mock() self.driver.create_node.return_value = self.node mock_ssh_module.have_paramiko = True deploy = Mock() node = self.driver.deploy_node(deploy=deploy) self.assertEqual(self.node.id, node.id) @patch('libcloud.compute.base.atexit') @patch('libcloud.compute.base.SSHClient') @patch('libcloud.compute.ssh') def test_deploy_node_at_exit_func_functionality(self, mock_ssh_module, _, mock_at_exit): self.driver.create_node = Mock() self.driver.create_node.return_value = self.node mock_ssh_module.have_paramiko = True deploy = Mock() def mock_at_exit_func(driver, node): pass # On success, at exit handler should be unregistered self.assertEqual(mock_at_exit.register.call_count, 0) self.assertEqual(mock_at_exit.unregister.call_count, 0) node = self.driver.deploy_node(deploy=deploy, at_exit_func=mock_at_exit_func) self.assertEqual(mock_at_exit.register.call_count, 1) self.assertEqual(mock_at_exit.unregister.call_count, 1) self.assertEqual(self.node.id, node.id) # On deploy failure, at exit handler should also be unregistered mock_at_exit.reset_mock() deploy.run.side_effect = Exception('foo') self.assertEqual(mock_at_exit.register.call_count, 0) self.assertEqual(mock_at_exit.unregister.call_count, 0) try: self.driver.deploy_node(deploy=deploy, at_exit_func=mock_at_exit_func) except DeploymentError as e: self.assertTrue(e.node.id, self.node.id) else: self.fail('Exception was not thrown') self.assertEqual(mock_at_exit.register.call_count, 1) self.assertEqual(mock_at_exit.unregister.call_count, 1) # But it should not be registered on create_node exception mock_at_exit.reset_mock() self.driver.create_node = Mock(side_effect=Exception('Failure')) self.assertEqual(mock_at_exit.register.call_count, 0) self.assertEqual(mock_at_exit.unregister.call_count, 0) try: self.driver.deploy_node(deploy=deploy, at_exit_func=mock_at_exit_func) except Exception as e: self.assertTrue('Failure' in str(e)) else: self.fail('Exception was not thrown') self.assertEqual(mock_at_exit.register.call_count, 0) self.assertEqual(mock_at_exit.unregister.call_count, 0) @patch('libcloud.compute.base.SSHClient') @patch('libcloud.compute.ssh') def test_deploy_node_deploy_node_kwargs_except_auth_are_not_propagated_on( self, mock_ssh_module, _): # Verify that keyword arguments which are specific to deploy_node() # are not propagated to create_node() mock_ssh_module.have_paramiko = True self.driver.create_node = Mock() self.driver.create_node.return_value = self.node self.driver._connect_and_run_deployment_script = Mock() self.driver._wait_until_running = Mock() kwargs = {} for key in DEPLOY_NODE_KWARGS: kwargs[key] = key kwargs['ssh_interface'] = 'public_ips' kwargs['ssh_alternate_usernames'] = ['foo', 'bar'] kwargs['timeout'] = 10 auth = NodeAuthPassword('P@$$w0rd') node = self.driver.deploy_node(name='name', image='image', size='size', auth=auth, ex_foo='ex_foo', **kwargs) self.assertEqual(self.node.id, node.id) self.assertEqual(self.driver.create_node.call_count, 1) call_kwargs = self.driver.create_node.call_args_list[0][1] expected_call_kwargs = { 'name': 'name', 'image': 'image', 'size': 'size', 'auth': auth, 'ex_foo': 'ex_foo' } self.assertEqual(expected_call_kwargs, call_kwargs) # If driver throws an exception it should fall back to passing in all # 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 call_count += 1 if call_count == 1: msg = 'create_node() takes at least 5 arguments (7 given)' raise TypeError(msg) return self.node self.driver.create_node = create_node node = self.driver.deploy_node(name='name', image='image', size='size', auth=auth, ex_foo='ex_foo', ex_custom_arg_1='a', ex_custom_arg_2='b', **kwargs) self.assertEqual(self.node.id, node.id) self.assertEqual(call_count, 2) 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 call_count += 1 if call_count == 1: msg = 'create_node() missing 3 required positional arguments' raise TypeError(msg) return self.node self.driver.create_node = create_node node = self.driver.deploy_node(name='name', image='image', size='size', auth=auth, ex_foo='ex_foo', ex_custom_arg_1='a', ex_custom_arg_2='b', **kwargs) self.assertEqual(self.node.id, node.id) self.assertEqual(call_count, 2) @patch('libcloud.compute.base.SSHClient') @patch('libcloud.compute.ssh') def test_deploy_node_exception_run_deployment_script( self, mock_ssh_module, _): self.driver.create_node = Mock() self.driver.create_node.return_value = self.node mock_ssh_module.have_paramiko = True deploy = Mock() deploy.run = Mock() deploy.run.side_effect = Exception('foo') try: self.driver.deploy_node(deploy=deploy) except DeploymentError as e: self.assertTrue(e.node.id, self.node.id) else: self.fail('Exception was not thrown') @patch('libcloud.compute.base.SSHClient') @patch('libcloud.compute.ssh') def test_deploy_node_exception_ssh_client_connect(self, mock_ssh_module, ssh_client): self.driver.create_node = Mock() self.driver.create_node.return_value = self.node mock_ssh_module.have_paramiko = True deploy = Mock() ssh_client.side_effect = IOError('bar') try: self.driver.deploy_node(deploy=deploy) except DeploymentError as e: self.assertTrue(e.node.id, self.node.id) else: self.fail('Exception was not thrown') @patch('libcloud.compute.ssh') def test_deploy_node_depoy_node_not_implemented(self, mock_ssh_module): self.driver.features = {'create_node': []} mock_ssh_module.have_paramiko = True try: self.driver.deploy_node(deploy=Mock()) except NotImplementedError: pass else: self.fail('Exception was not thrown') self.driver.features = {} try: self.driver.deploy_node(deploy=Mock()) except NotImplementedError: pass else: self.fail('Exception was not thrown') @patch('libcloud.compute.base.SSHClient') @patch('libcloud.compute.ssh') def test_deploy_node_password_auth(self, mock_ssh_module, _): self.driver.features = {'create_node': ['password']} mock_ssh_module.have_paramiko = True self.driver.create_node = Mock() self.driver.create_node.return_value = self.node node = self.driver.deploy_node(deploy=Mock()) self.assertEqual(self.node.id, node.id) @patch('libcloud.compute.base.SSHClient') @patch('libcloud.compute.ssh') def test_exception_is_thrown_is_paramiko_is_not_available( self, mock_ssh_module, _): self.driver.features = {'create_node': ['password']} self.driver.create_node = Mock() self.driver.create_node.return_value = self.node mock_ssh_module.have_paramiko = False try: self.driver.deploy_node(deploy=Mock()) except RuntimeError as e: self.assertTrue(str(e).find('paramiko is not installed') != -1) else: self.fail('Exception was not thrown') mock_ssh_module.have_paramiko = True node = self.driver.deploy_node(deploy=Mock()) self.assertEqual(self.node.id, node.id)
class DeploymentTests(unittest.TestCase): def setUp(self): Rackspace.connectionCls.conn_classes = (None, RackspaceMockHttp) RackspaceMockHttp.type = None self.driver = Rackspace(*RACKSPACE_PARAMS) # normally authentication happens lazily, but we force it here self.driver.connection._populate_hosts_and_request_paths() self.driver.features = {"create_node": ["generates_password"]} self.node = Node( id=12345, name="test", state=NodeState.RUNNING, public_ips=["1.2.3.4"], private_ips=["1.2.3.5"], driver=Rackspace, ) self.node2 = Node( id=123456, name="test", state=NodeState.RUNNING, public_ips=["1.2.3.4"], private_ips=["1.2.3.5"], driver=Rackspace, ) def test_multi_step_deployment(self): msd = MultiStepDeployment() self.assertEqual(len(msd.steps), 0) msd.add(MockDeployment()) self.assertEqual(len(msd.steps), 1) self.assertEqual(self.node, msd.run(node=self.node, client=None)) def test_ssh_key_deployment(self): sshd = SSHKeyDeployment(key="1234") self.assertEqual(self.node, sshd.run(node=self.node, client=MockClient(hostname="localhost"))) def test_file_deployment(self): # use this file (__file__) for obtaining permissions target = os.path.join("/tmp", os.path.basename(__file__)) fd = FileDeployment(__file__, target) self.assertEqual(target, fd.target) self.assertEqual(__file__, fd.source) self.assertEqual(self.node, fd.run(node=self.node, client=MockClient(hostname="localhost"))) def test_script_deployment(self): sd1 = ScriptDeployment(script="foobar", delete=True) sd2 = ScriptDeployment(script="foobar", delete=False) sd3 = ScriptDeployment(script="foobar", delete=False, name="foobarname") self.assertTrue(sd1.name.find("deployment") != "1") self.assertEqual(sd3.name, "foobarname") 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"))) def test_script_file_deployment(self): file_path = os.path.abspath(__file__) with open(file_path, "rb") as fp: content = fp.read() if PY3: content = content.decode("utf-8") sfd1 = ScriptFileDeployment(script_file=file_path) self.assertEqual(sfd1.script, content) def test_script_deployment_relative_path(self): client = Mock() client.put.return_value = "/home/ubuntu/relative.sh" client.run.return_value = ("", "", 0) sd = ScriptDeployment(script='echo "foo"', name="relative.sh") sd.run(self.node, client) client.run.assert_called_once_with("/home/ubuntu/relative.sh") def test_script_deployment_absolute_path(self): client = Mock() client.put.return_value = "/home/ubuntu/relative.sh" client.run.return_value = ("", "", 0) sd = ScriptDeployment(script='echo "foo"', name="/root/relative.sh") sd.run(self.node, client) client.run.assert_called_once_with("/root/relative.sh") def test_script_deployment_with_arguments(self): client = Mock() client.put.return_value = "/home/ubuntu/relative.sh" client.run.return_value = ("", "", 0) args = ["arg1", "arg2", "--option1=test"] sd = ScriptDeployment(script='echo "foo"', args=args, name="/root/relative.sh") sd.run(self.node, client) expected = "/root/relative.sh arg1 arg2 --option1=test" client.run.assert_called_once_with(expected) client.reset_mock() args = [] sd = ScriptDeployment(script='echo "foo"', args=args, name="/root/relative.sh") sd.run(self.node, client) expected = "/root/relative.sh" client.run.assert_called_once_with(expected) def test_script_file_deployment_with_arguments(self): file_path = os.path.abspath(__file__) client = Mock() client.put.return_value = "/home/ubuntu/relative.sh" client.run.return_value = ("", "", 0) args = ["arg1", "arg2", "--option1=test", "option2"] sfd = ScriptFileDeployment(script_file=file_path, args=args, name="/root/relative.sh") sfd.run(self.node, client) expected = "/root/relative.sh arg1 arg2 --option1=test option2" client.run.assert_called_once_with(expected) def test_script_deployment_and_sshkey_deployment_argument_types(self): class FileObject(object): def __init__(self, name): self.name = name def read(self): return "bar" ScriptDeployment(script="foobar") ScriptDeployment(script=u("foobar")) ScriptDeployment(script=FileObject("test")) SSHKeyDeployment(key="foobar") SSHKeyDeployment(key=u("foobar")) SSHKeyDeployment(key=FileObject("test")) try: ScriptDeployment(script=[]) except TypeError: pass else: self.fail("TypeError was not thrown") try: SSHKeyDeployment(key={}) except TypeError: pass else: self.fail("TypeError was not thrown") def test_wait_until_running_running_instantly(self): node2, ips = self.driver.wait_until_running(nodes=[self.node], wait_period=1, timeout=0.5)[0] self.assertEqual(self.node.uuid, node2.uuid) self.assertEqual(["67.23.21.33"], ips) def test_wait_until_running_running_after_1_second(self): RackspaceMockHttp.type = "05_SECOND_DELAY" node2, ips = self.driver.wait_until_running(nodes=[self.node], wait_period=1, timeout=0.5)[0] self.assertEqual(self.node.uuid, node2.uuid) self.assertEqual(["67.23.21.33"], ips) def test_wait_until_running_running_after_1_second_private_ips(self): RackspaceMockHttp.type = "05_SECOND_DELAY" node2, ips = self.driver.wait_until_running( nodes=[self.node], wait_period=1, timeout=0.5, ssh_interface="private_ips" )[0] self.assertEqual(self.node.uuid, node2.uuid) self.assertEqual(["10.176.168.218"], ips) def test_wait_until_running_invalid_ssh_interface_argument(self): try: self.driver.wait_until_running(nodes=[self.node], wait_period=1, ssh_interface="invalid") except ValueError: pass else: self.fail("Exception was not thrown") def test_wait_until_running_timeout(self): RackspaceMockHttp.type = "TIMEOUT" try: self.driver.wait_until_running(nodes=[self.node], wait_period=0.1, timeout=0.5) except LibcloudError: e = sys.exc_info()[1] self.assertTrue(e.value.find("Timed out") != -1) else: self.fail("Exception was not thrown") def test_wait_until_running_running_node_missing_from_list_nodes(self): RackspaceMockHttp.type = "MISSING" try: self.driver.wait_until_running(nodes=[self.node], wait_period=0.1, timeout=0.5) except LibcloudError: e = sys.exc_info()[1] self.assertTrue(e.value.find("Timed out after 0.5 second") != -1) else: self.fail("Exception was not thrown") def test_wait_until_running_running_multiple_nodes_have_same_uuid(self): RackspaceMockHttp.type = "SAME_UUID" try: self.driver.wait_until_running(nodes=[self.node], wait_period=0.1, timeout=0.5) except LibcloudError: e = sys.exc_info()[1] self.assertTrue(e.value.find("Unable to match specified uuids") != -1) else: self.fail("Exception was not thrown") def test_wait_until_running_running_wait_for_multiple_nodes(self): RackspaceMockHttp.type = "MULTIPLE_NODES" nodes = self.driver.wait_until_running(nodes=[self.node, self.node2], wait_period=0.1, timeout=0.5) self.assertEqual(self.node.uuid, nodes[0][0].uuid) self.assertEqual(self.node2.uuid, nodes[1][0].uuid) self.assertEqual(["67.23.21.33"], nodes[0][1]) self.assertEqual(["67.23.21.34"], nodes[1][1]) def test_ssh_client_connect_success(self): mock_ssh_client = Mock() mock_ssh_client.return_value = None ssh_client = self.driver._ssh_client_connect(ssh_client=mock_ssh_client, timeout=0.5) self.assertEqual(mock_ssh_client, ssh_client) def test_ssh_client_connect_timeout(self): mock_ssh_client = Mock() mock_ssh_client.connect = Mock() mock_ssh_client.connect.side_effect = IOError("bam") try: self.driver._ssh_client_connect(ssh_client=mock_ssh_client, timeout=0.5) except LibcloudError: e = sys.exc_info()[1] self.assertTrue(e.value.find("Giving up") != -1) else: self.fail("Exception was not thrown") def test_run_deployment_script_success(self): task = Mock() ssh_client = Mock() ssh_client2 = self.driver._run_deployment_script(task=task, node=self.node, ssh_client=ssh_client, max_tries=2) self.assertTrue(isinstance(ssh_client2, Mock)) def test_run_deployment_script_exception(self): task = Mock() task.run = Mock() task.run.side_effect = Exception("bar") ssh_client = Mock() try: self.driver._run_deployment_script(task=task, node=self.node, ssh_client=ssh_client, max_tries=2) except LibcloudError: e = sys.exc_info()[1] self.assertTrue(e.value.find("Failed after 2 tries") != -1) else: self.fail("Exception was not thrown") @patch("libcloud.compute.base.SSHClient") @patch("libcloud.compute.ssh") def test_deploy_node_success(self, mock_ssh_module, _): self.driver.create_node = Mock() self.driver.create_node.return_value = self.node mock_ssh_module.have_paramiko = True deploy = Mock() node = self.driver.deploy_node(deploy=deploy) self.assertEqual(self.node.id, node.id) @patch("libcloud.compute.base.SSHClient") @patch("libcloud.compute.ssh") def test_deploy_node_exception_run_deployment_script(self, mock_ssh_module, _): self.driver.create_node = Mock() self.driver.create_node.return_value = self.node mock_ssh_module.have_paramiko = True deploy = Mock() deploy.run = Mock() deploy.run.side_effect = Exception("foo") try: self.driver.deploy_node(deploy=deploy) except DeploymentError: e = sys.exc_info()[1] self.assertTrue(e.node.id, self.node.id) else: self.fail("Exception was not thrown") @patch("libcloud.compute.base.SSHClient") @patch("libcloud.compute.ssh") def test_deploy_node_exception_ssh_client_connect(self, mock_ssh_module, ssh_client): self.driver.create_node = Mock() self.driver.create_node.return_value = self.node mock_ssh_module.have_paramiko = True deploy = Mock() ssh_client.side_effect = IOError("bar") try: self.driver.deploy_node(deploy=deploy) except DeploymentError: e = sys.exc_info()[1] self.assertTrue(e.node.id, self.node.id) else: self.fail("Exception was not thrown") @patch("libcloud.compute.ssh") def test_deploy_node_depoy_node_not_implemented(self, mock_ssh_module): self.driver.features = {"create_node": []} mock_ssh_module.have_paramiko = True try: self.driver.deploy_node(deploy=Mock()) except NotImplementedError: pass else: self.fail("Exception was not thrown") self.driver.features = {} try: self.driver.deploy_node(deploy=Mock()) except NotImplementedError: pass else: self.fail("Exception was not thrown") @patch("libcloud.compute.base.SSHClient") @patch("libcloud.compute.ssh") def test_deploy_node_password_auth(self, mock_ssh_module, _): self.driver.features = {"create_node": ["password"]} mock_ssh_module.have_paramiko = True self.driver.create_node = Mock() self.driver.create_node.return_value = self.node node = self.driver.deploy_node(deploy=Mock()) self.assertEqual(self.node.id, node.id) @patch("libcloud.compute.base.SSHClient") @patch("libcloud.compute.ssh") def test_exception_is_thrown_is_paramiko_is_not_available(self, mock_ssh_module, _): self.driver.features = {"create_node": ["password"]} self.driver.create_node = Mock() self.driver.create_node.return_value = self.node mock_ssh_module.have_paramiko = False try: self.driver.deploy_node(deploy=Mock()) except RuntimeError: e = sys.exc_info()[1] self.assertTrue(str(e).find("paramiko is not installed") != -1) else: self.fail("Exception was not thrown") mock_ssh_module.have_paramiko = True node = self.driver.deploy_node(deploy=Mock()) self.assertEqual(self.node.id, node.id)
class DeploymentTests(unittest.TestCase): def setUp(self): Rackspace.connectionCls.conn_classes = (None, RackspaceMockHttp) RackspaceMockHttp.type = None self.driver = Rackspace(*RACKSPACE_PARAMS) # normally authentication happens lazily, but we force it here self.driver.connection._populate_hosts_and_request_paths() self.driver.features = {'create_node': ['generates_password']} self.node = Node(id=12345, name='test', state=NodeState.RUNNING, public_ips=['1.2.3.4'], private_ips=['1.2.3.5'], driver=Rackspace) self.node2 = Node(id=123456, name='test', state=NodeState.RUNNING, public_ips=['1.2.3.4'], private_ips=['1.2.3.5'], driver=Rackspace) def test_multi_step_deployment(self): msd = MultiStepDeployment() self.assertEqual(len(msd.steps), 0) msd.add(MockDeployment()) self.assertEqual(len(msd.steps), 1) self.assertEqual(self.node, msd.run(node=self.node, client=None)) def test_ssh_key_deployment(self): sshd = SSHKeyDeployment(key='1234') self.assertEqual(self.node, sshd.run(node=self.node, client=MockClient(hostname='localhost'))) def test_file_deployment(self): # use this file (__file__) for obtaining permissions target = os.path.join('/tmp', os.path.basename(__file__)) fd = FileDeployment(__file__, target) self.assertEqual(target, fd.target) self.assertEqual(__file__, fd.source) self.assertEqual(self.node, fd.run( node=self.node, client=MockClient(hostname='localhost'))) def test_script_deployment(self): sd1 = ScriptDeployment(script='foobar', delete=True) sd2 = ScriptDeployment(script='foobar', delete=False) sd3 = ScriptDeployment( script='foobar', delete=False, name='foobarname') self.assertTrue(sd1.name.find('deployment') != '1') self.assertEqual(sd3.name, 'foobarname') 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'))) def test_script_file_deployment(self): file_path = os.path.abspath(__file__) with open(file_path, 'rb') as fp: content = fp.read() if PY3: content = content.decode('utf-8') sfd1 = ScriptFileDeployment(script_file=file_path) self.assertEqual(sfd1.script, content) def test_script_deployment_relative_path(self): client = Mock() client.put.return_value = '/home/ubuntu/relative.sh' client.run.return_value = ('', '', 0) sd = ScriptDeployment(script='echo "foo"', name='relative.sh') sd.run(self.node, client) client.run.assert_called_once_with('/home/ubuntu/relative.sh') def test_script_deployment_absolute_path(self): client = Mock() client.put.return_value = '/home/ubuntu/relative.sh' client.run.return_value = ('', '', 0) sd = ScriptDeployment(script='echo "foo"', name='/root/relative.sh') sd.run(self.node, client) client.run.assert_called_once_with('/root/relative.sh') def test_script_deployment_with_arguments(self): client = Mock() client.put.return_value = '/home/ubuntu/relative.sh' client.run.return_value = ('', '', 0) args = ['arg1', 'arg2', '--option1=test'] sd = ScriptDeployment(script='echo "foo"', args=args, name='/root/relative.sh') sd.run(self.node, client) expected = '/root/relative.sh arg1 arg2 --option1=test' client.run.assert_called_once_with(expected) client.reset_mock() args = [] sd = ScriptDeployment(script='echo "foo"', args=args, name='/root/relative.sh') sd.run(self.node, client) expected = '/root/relative.sh' client.run.assert_called_once_with(expected) def test_script_file_deployment_with_arguments(self): file_path = os.path.abspath(__file__) client = Mock() client.put.return_value = '/home/ubuntu/relative.sh' client.run.return_value = ('', '', 0) args = ['arg1', 'arg2', '--option1=test', 'option2'] sfd = ScriptFileDeployment(script_file=file_path, args=args, name='/root/relative.sh') sfd.run(self.node, client) expected = '/root/relative.sh arg1 arg2 --option1=test option2' client.run.assert_called_once_with(expected) def test_script_deployment_and_sshkey_deployment_argument_types(self): class FileObject(object): def __init__(self, name): self.name = name def read(self): return 'bar' ScriptDeployment(script='foobar') ScriptDeployment(script=u('foobar')) ScriptDeployment(script=FileObject('test')) SSHKeyDeployment(key='foobar') SSHKeyDeployment(key=u('foobar')) SSHKeyDeployment(key=FileObject('test')) try: ScriptDeployment(script=[]) except TypeError: pass else: self.fail('TypeError was not thrown') try: SSHKeyDeployment(key={}) except TypeError: pass else: self.fail('TypeError was not thrown') def test_wait_until_running_running_instantly(self): node2, ips = self.driver.wait_until_running( nodes=[self.node], wait_period=1, timeout=0.5)[0] self.assertEqual(self.node.uuid, node2.uuid) self.assertEqual(['67.23.21.33'], ips) def test_wait_until_running_running_after_1_second(self): RackspaceMockHttp.type = '05_SECOND_DELAY' node2, ips = self.driver.wait_until_running( nodes=[self.node], wait_period=1, timeout=0.5)[0] self.assertEqual(self.node.uuid, node2.uuid) self.assertEqual(['67.23.21.33'], ips) def test_wait_until_running_running_after_1_second_private_ips(self): RackspaceMockHttp.type = '05_SECOND_DELAY' node2, ips = self.driver.wait_until_running( nodes=[self.node], wait_period=1, timeout=0.5, ssh_interface='private_ips')[0] self.assertEqual(self.node.uuid, node2.uuid) self.assertEqual(['10.176.168.218'], ips) def test_wait_until_running_invalid_ssh_interface_argument(self): try: self.driver.wait_until_running(nodes=[self.node], wait_period=1, ssh_interface='invalid') except ValueError: pass else: self.fail('Exception was not thrown') def test_wait_until_running_timeout(self): RackspaceMockHttp.type = 'TIMEOUT' try: self.driver.wait_until_running(nodes=[self.node], wait_period=0.1, timeout=0.5) except LibcloudError: e = sys.exc_info()[1] self.assertTrue(e.value.find('Timed out') != -1) else: self.fail('Exception was not thrown') def test_wait_until_running_running_node_missing_from_list_nodes(self): RackspaceMockHttp.type = 'MISSING' try: self.driver.wait_until_running(nodes=[self.node], wait_period=0.1, timeout=0.5) except LibcloudError: e = sys.exc_info()[1] self.assertTrue(e.value.find('Timed out after 0.5 second') != -1) else: self.fail('Exception was not thrown') def test_wait_until_running_running_multiple_nodes_have_same_uuid(self): RackspaceMockHttp.type = 'SAME_UUID' try: self.driver.wait_until_running(nodes=[self.node], wait_period=0.1, timeout=0.5) except LibcloudError: e = sys.exc_info()[1] self.assertTrue( e.value.find('Unable to match specified uuids') != -1) else: self.fail('Exception was not thrown') def test_wait_until_running_running_wait_for_multiple_nodes(self): RackspaceMockHttp.type = 'MULTIPLE_NODES' nodes = self.driver.wait_until_running( nodes=[self.node, self.node2], wait_period=0.1, timeout=0.5) self.assertEqual(self.node.uuid, nodes[0][0].uuid) self.assertEqual(self.node2.uuid, nodes[1][0].uuid) self.assertEqual(['67.23.21.33'], nodes[0][1]) self.assertEqual(['67.23.21.34'], nodes[1][1]) def test_ssh_client_connect_success(self): mock_ssh_client = Mock() mock_ssh_client.return_value = None ssh_client = self.driver._ssh_client_connect( ssh_client=mock_ssh_client, timeout=0.5) self.assertEqual(mock_ssh_client, ssh_client) def test_ssh_client_connect_timeout(self): mock_ssh_client = Mock() mock_ssh_client.connect = Mock() mock_ssh_client.connect.side_effect = IOError('bam') try: self.driver._ssh_client_connect(ssh_client=mock_ssh_client, timeout=0.5) except LibcloudError: e = sys.exc_info()[1] self.assertTrue(e.value.find('Giving up') != -1) else: self.fail('Exception was not thrown') def test_run_deployment_script_success(self): task = Mock() ssh_client = Mock() ssh_client2 = self.driver._run_deployment_script(task=task, node=self.node, ssh_client=ssh_client, max_tries=2) self.assertTrue(isinstance(ssh_client2, Mock)) def test_run_deployment_script_exception(self): task = Mock() task.run = Mock() task.run.side_effect = Exception('bar') ssh_client = Mock() try: self.driver._run_deployment_script(task=task, node=self.node, ssh_client=ssh_client, max_tries=2) except LibcloudError: e = sys.exc_info()[1] self.assertTrue(e.value.find('Failed after 2 tries') != -1) else: self.fail('Exception was not thrown') @patch('libcloud.compute.base.SSHClient') @patch('libcloud.compute.ssh') def test_deploy_node_success(self, mock_ssh_module, _): self.driver.create_node = Mock() self.driver.create_node.return_value = self.node mock_ssh_module.have_paramiko = True deploy = Mock() node = self.driver.deploy_node(deploy=deploy) self.assertEqual(self.node.id, node.id) @patch('libcloud.compute.base.SSHClient') @patch('libcloud.compute.ssh') def test_deploy_node_exception_run_deployment_script(self, mock_ssh_module, _): self.driver.create_node = Mock() self.driver.create_node.return_value = self.node mock_ssh_module.have_paramiko = True deploy = Mock() deploy.run = Mock() deploy.run.side_effect = Exception('foo') try: self.driver.deploy_node(deploy=deploy) except DeploymentError: e = sys.exc_info()[1] self.assertTrue(e.node.id, self.node.id) else: self.fail('Exception was not thrown') @patch('libcloud.compute.base.SSHClient') @patch('libcloud.compute.ssh') def test_deploy_node_exception_ssh_client_connect(self, mock_ssh_module, ssh_client): self.driver.create_node = Mock() self.driver.create_node.return_value = self.node mock_ssh_module.have_paramiko = True deploy = Mock() ssh_client.side_effect = IOError('bar') try: self.driver.deploy_node(deploy=deploy) except DeploymentError: e = sys.exc_info()[1] self.assertTrue(e.node.id, self.node.id) else: self.fail('Exception was not thrown') @patch('libcloud.compute.ssh') def test_deploy_node_depoy_node_not_implemented(self, mock_ssh_module): self.driver.features = {'create_node': []} mock_ssh_module.have_paramiko = True try: self.driver.deploy_node(deploy=Mock()) except NotImplementedError: pass else: self.fail('Exception was not thrown') self.driver.features = {} try: self.driver.deploy_node(deploy=Mock()) except NotImplementedError: pass else: self.fail('Exception was not thrown') @patch('libcloud.compute.base.SSHClient') @patch('libcloud.compute.ssh') def test_deploy_node_password_auth(self, mock_ssh_module, _): self.driver.features = {'create_node': ['password']} mock_ssh_module.have_paramiko = True self.driver.create_node = Mock() self.driver.create_node.return_value = self.node node = self.driver.deploy_node(deploy=Mock()) self.assertEqual(self.node.id, node.id) @patch('libcloud.compute.base.SSHClient') @patch('libcloud.compute.ssh') def test_exception_is_thrown_is_paramiko_is_not_available(self, mock_ssh_module, _): self.driver.features = {'create_node': ['password']} self.driver.create_node = Mock() self.driver.create_node.return_value = self.node mock_ssh_module.have_paramiko = False try: self.driver.deploy_node(deploy=Mock()) except RuntimeError: e = sys.exc_info()[1] self.assertTrue(str(e).find('paramiko is not installed') != -1) else: self.fail('Exception was not thrown') mock_ssh_module.have_paramiko = True node = self.driver.deploy_node(deploy=Mock()) self.assertEqual(self.node.id, node.id)
class DeploymentTests(unittest.TestCase): def setUp(self): Rackspace.connectionCls.conn_class = RackspaceMockHttp RackspaceMockHttp.type = None self.driver = Rackspace(*RACKSPACE_PARAMS) # normally authentication happens lazily, but we force it here self.driver.connection._populate_hosts_and_request_paths() self.driver.features = {"create_node": ["generates_password"]} self.node = Node( id=12345, name="test", state=NodeState.RUNNING, public_ips=["1.2.3.4"], private_ips=["1.2.3.5"], driver=Rackspace, ) self.node2 = Node( id=123456, name="test", state=NodeState.RUNNING, public_ips=["1.2.3.4"], private_ips=["1.2.3.5"], driver=Rackspace, ) def test_multi_step_deployment(self): msd = MultiStepDeployment() self.assertEqual(len(msd.steps), 0) msd.add(MockDeployment()) self.assertEqual(len(msd.steps), 1) self.assertEqual(self.node, msd.run(node=self.node, client=None)) def test_ssh_key_deployment(self): sshd = SSHKeyDeployment(key="1234") self.assertEqual( self.node, sshd.run(node=self.node, client=MockClient(hostname="localhost"))) def test_file_deployment(self): # use this file (__file__) for obtaining permissions target = os.path.join("/tmp", os.path.basename(__file__)) fd = FileDeployment(__file__, target) self.assertEqual(target, fd.target) self.assertEqual(__file__, fd.source) self.assertEqual( self.node, fd.run(node=self.node, client=MockClient(hostname="localhost"))) def test_script_deployment(self): sd1 = ScriptDeployment(script="foobar", delete=True) 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__) with open(file_path, "rb") as fp: content = fp.read() if PY3: content = content.decode("utf-8") 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() client.put.return_value = FILE_PATH client.run.return_value = ("", "", 0) sd = ScriptDeployment(script='echo "foo"', name="relative.sh") sd.run(self.node, client) client.run.assert_called_once_with(FILE_PATH, timeout=None) def test_script_deployment_absolute_path(self): file_path = "{0}root{0}relative.sh".format(os.path.sep) client = Mock() client.put.return_value = file_path client.run.return_value = ("", "", 0) sd = ScriptDeployment(script='echo "foo"', name=file_path) sd.run(self.node, client) client.run.assert_called_once_with(file_path, timeout=None) def test_script_deployment_with_arguments(self): file_path = "{0}root{0}relative.sh".format(os.path.sep) client = Mock() client.put.return_value = file_path client.run.return_value = ("", "", 0) args = ["arg1", "arg2", "--option1=test"] sd = ScriptDeployment(script='echo "foo"', args=args, name=file_path) sd.run(self.node, client) expected = "%s arg1 arg2 --option1=test" % (file_path) client.run.assert_called_once_with(expected, timeout=None) client.reset_mock() args = [] sd = ScriptDeployment(script='echo "foo"', args=args, name=file_path) sd.run(self.node, client) expected = file_path client.run.assert_called_once_with(expected, timeout=None) def test_script_file_deployment_with_arguments(self): file_path = os.path.abspath(__file__) client = Mock() client.put.return_value = FILE_PATH client.run.return_value = ("", "", 0) args = ["arg1", "arg2", "--option1=test", "option2"] sfd = ScriptFileDeployment(script_file=file_path, args=args, name=file_path) sfd.run(self.node, client) expected = "%s arg1 arg2 --option1=test option2" % (file_path) client.run.assert_called_once_with(expected, timeout=None) def test_script_deployment_and_sshkey_deployment_argument_types(self): class FileObject(object): def __init__(self, name): self.name = name def read(self): return "bar" ScriptDeployment(script="foobar") ScriptDeployment(script=u("foobar")) ScriptDeployment(script=FileObject("test")) SSHKeyDeployment(key="foobar") SSHKeyDeployment(key=u("foobar")) SSHKeyDeployment(key=FileObject("test")) try: ScriptDeployment(script=[]) except TypeError: pass else: self.fail("TypeError was not thrown") try: SSHKeyDeployment(key={}) except TypeError: pass else: self.fail("TypeError was not thrown") def test_wait_until_running_running_instantly(self): node2, ips = self.driver.wait_until_running(nodes=[self.node], wait_period=0.1, timeout=0.5)[0] self.assertEqual(self.node.uuid, node2.uuid) self.assertEqual(["67.23.21.33"], ips) def test_wait_until_running_without_ip(self): RackspaceMockHttp.type = "NO_IP" try: node2, ips = self.driver.wait_until_running(nodes=[self.node], wait_period=0.1, timeout=0.2)[0] except LibcloudError as e: self.assertTrue(e.value.find("Timed out after 0.2 second") != -1) else: self.fail("Exception was not thrown") def test_wait_until_running_with_only_ipv6(self): RackspaceMockHttp.type = "IPV6" try: node2, ips = self.driver.wait_until_running(nodes=[self.node], wait_period=0.1, timeout=0.2)[0] except LibcloudError as e: self.assertTrue(e.value.find("Timed out after 0.2 second") != -1) else: self.fail("Exception was not thrown") def test_wait_until_running_with_ipv6_ok(self): RackspaceMockHttp.type = "IPV6" node2, ips = self.driver.wait_until_running(nodes=[self.node], wait_period=1, force_ipv4=False, timeout=0.5)[0] self.assertEqual(self.node.uuid, node2.uuid) self.assertEqual(["2001:DB8::1"], ips) def test_wait_until_running_running_after_0_2_second(self): RackspaceMockHttp.type = "05_SECOND_DELAY" node2, ips = self.driver.wait_until_running(nodes=[self.node], wait_period=0.2, timeout=0.1)[0] self.assertEqual(self.node.uuid, node2.uuid) self.assertEqual(["67.23.21.33"], ips) def test_wait_until_running_running_after_0_2_second_private_ips(self): RackspaceMockHttp.type = "05_SECOND_DELAY" node2, ips = self.driver.wait_until_running( nodes=[self.node], wait_period=0.2, timeout=0.1, ssh_interface="private_ips")[0] self.assertEqual(self.node.uuid, node2.uuid) self.assertEqual(["10.176.168.218"], ips) def test_wait_until_running_invalid_ssh_interface_argument(self): try: self.driver.wait_until_running(nodes=[self.node], wait_period=1, ssh_interface="invalid") except ValueError: pass else: self.fail("Exception was not thrown") def test_wait_until_running_timeout(self): RackspaceMockHttp.type = "TIMEOUT" try: self.driver.wait_until_running(nodes=[self.node], wait_period=0.1, timeout=0.2) except LibcloudError as e: self.assertTrue(e.value.find("Timed out") != -1) else: self.fail("Exception was not thrown") def test_wait_until_running_running_node_missing_from_list_nodes(self): RackspaceMockHttp.type = "MISSING" try: self.driver.wait_until_running(nodes=[self.node], wait_period=0.1, timeout=0.2) except LibcloudError as e: self.assertTrue(e.value.find("Timed out after 0.2 second") != -1) else: self.fail("Exception was not thrown") def test_wait_until_running_running_multiple_nodes_have_same_uuid(self): RackspaceMockHttp.type = "SAME_UUID" try: self.driver.wait_until_running(nodes=[self.node], wait_period=0.1, timeout=0.2) except LibcloudError as e: self.assertTrue( e.value.find("Unable to match specified uuids") != -1) else: self.fail("Exception was not thrown") def test_wait_until_running_running_wait_for_multiple_nodes(self): RackspaceMockHttp.type = "MULTIPLE_NODES" nodes = self.driver.wait_until_running(nodes=[self.node, self.node2], wait_period=0.1, timeout=0.5) self.assertEqual(self.node.uuid, nodes[0][0].uuid) self.assertEqual(self.node2.uuid, nodes[1][0].uuid) self.assertEqual(["67.23.21.33"], nodes[0][1]) self.assertEqual(["67.23.21.34"], nodes[1][1]) def test_ssh_client_connect_success(self): mock_ssh_client = Mock() mock_ssh_client.return_value = None ssh_client = self.driver._ssh_client_connect( ssh_client=mock_ssh_client, timeout=0.5) self.assertEqual(mock_ssh_client, ssh_client) def test_ssh_client_connect_timeout(self): mock_ssh_client = Mock() mock_ssh_client.connect = Mock() mock_ssh_client.connect.side_effect = IOError("bam") try: self.driver._ssh_client_connect(ssh_client=mock_ssh_client, wait_period=0.1, timeout=0.2) except LibcloudError as e: self.assertTrue(e.value.find("Giving up") != -1) else: self.fail("Exception was not thrown") @unittest.skipIf(not have_paramiko, "Skipping because paramiko is not available") def test_ssh_client_connect_immediately_throws_on_fatal_execption(self): # Verify that fatal exceptions are immediately propagated and ensure # we don't try to retry on them from paramiko.ssh_exception import SSHException from paramiko.ssh_exception import PasswordRequiredException mock_ssh_client = Mock() mock_ssh_client.connect = Mock() mock_ssh_client.connect.side_effect = IOError("bam") mock_exceptions = [ SSHException("Invalid or unsupported key type"), PasswordRequiredException("private key file is encrypted"), SSHException("OpenSSH private key file checkints do not match"), ] for mock_exception in mock_exceptions: mock_ssh_client.connect = Mock(side_effect=mock_exception) assertRaisesRegex( self, mock_exception.__class__, str(mock_exception), self.driver._ssh_client_connect, ssh_client=mock_ssh_client, wait_period=0.1, timeout=0.2, ) def test_run_deployment_script_success(self): task = Mock() ssh_client = Mock() ssh_client2 = self.driver._run_deployment_script(task=task, node=self.node, ssh_client=ssh_client, max_tries=2) self.assertTrue(isinstance(ssh_client2, Mock)) def test_run_deployment_script_exception(self): task = Mock() task.run = Mock() task.run.side_effect = Exception("bar") ssh_client = Mock() try: self.driver._run_deployment_script(task=task, node=self.node, ssh_client=ssh_client, max_tries=2) except LibcloudError as e: self.assertTrue(e.value.find("Failed after 2 tries") != -1) else: self.fail("Exception was not thrown") def test_run_deployment_script_ssh_command_timeout_fatal_exception(self): # We shouldn't retry on SSHCommandTimeoutError error since it's fatal task = Mock() task.run = Mock() task.run.side_effect = SSHCommandTimeoutError("ls -la", 10) ssh_client = Mock() try: self.driver._run_deployment_script(task=task, node=self.node, ssh_client=ssh_client, max_tries=5) except SSHCommandTimeoutError as e: self.assertTrue(e.message.find("Command didn't finish") != -1) else: self.fail("Exception was not thrown") def test_run_deployment_script_reconnect_on_ssh_session_not_active(self): # Verify that we try to reconnect if task.run() throws exception with # "SSH client not active" message global exception_counter exception_counter = 0 def mock_run(*args, **kwargs): # Mock run() method which throws "SSH session not active" exception # during first two calls and on third one it returns None. global exception_counter exception_counter += 1 if exception_counter > 2: return None raise Exception("SSH session not active") task = Mock() task.run = Mock() task.run = mock_run ssh_client = Mock() ssh_client.timeout = 20 self.assertEqual(ssh_client.connect.call_count, 0) self.assertEqual(ssh_client.close.call_count, 0) self.driver._run_deployment_script(task=task, node=self.node, ssh_client=ssh_client, max_tries=5) self.assertEqual(ssh_client.connect.call_count, 2) self.assertEqual(ssh_client.close.call_count, 2 + 1) @patch("libcloud.compute.base.SSHClient") @patch("libcloud.compute.ssh") def test_deploy_node_success(self, mock_ssh_module, _): self.driver.create_node = Mock() self.driver.create_node.return_value = self.node mock_ssh_module.have_paramiko = True deploy = Mock() node = self.driver.deploy_node(deploy=deploy) self.assertEqual(self.node.id, node.id) @patch("libcloud.compute.base.atexit") @patch("libcloud.compute.base.SSHClient") @patch("libcloud.compute.ssh") def test_deploy_node_at_exit_func_functionality(self, mock_ssh_module, _, mock_at_exit): self.driver.create_node = Mock() self.driver.create_node.return_value = self.node mock_ssh_module.have_paramiko = True deploy = Mock() def mock_at_exit_func(driver, node): pass # On success, at exit handler should be unregistered self.assertEqual(mock_at_exit.register.call_count, 0) self.assertEqual(mock_at_exit.unregister.call_count, 0) node = self.driver.deploy_node(deploy=deploy, at_exit_func=mock_at_exit_func) self.assertEqual(mock_at_exit.register.call_count, 1) self.assertEqual(mock_at_exit.unregister.call_count, 1) self.assertEqual(self.node.id, node.id) # On deploy failure, at exit handler should also be unregistered mock_at_exit.reset_mock() deploy.run.side_effect = Exception("foo") self.assertEqual(mock_at_exit.register.call_count, 0) self.assertEqual(mock_at_exit.unregister.call_count, 0) try: self.driver.deploy_node(deploy=deploy, at_exit_func=mock_at_exit_func) except DeploymentError as e: self.assertTrue(e.node.id, self.node.id) else: self.fail("Exception was not thrown") self.assertEqual(mock_at_exit.register.call_count, 1) self.assertEqual(mock_at_exit.unregister.call_count, 1) # But it should not be registered on create_node exception mock_at_exit.reset_mock() self.driver.create_node = Mock(side_effect=Exception("Failure")) self.assertEqual(mock_at_exit.register.call_count, 0) self.assertEqual(mock_at_exit.unregister.call_count, 0) try: self.driver.deploy_node(deploy=deploy, at_exit_func=mock_at_exit_func) except Exception as e: self.assertTrue("Failure" in str(e)) else: self.fail("Exception was not thrown") self.assertEqual(mock_at_exit.register.call_count, 0) self.assertEqual(mock_at_exit.unregister.call_count, 0) @patch("libcloud.compute.base.SSHClient") @patch("libcloud.compute.ssh") def test_deploy_node_deploy_node_kwargs_except_auth_are_not_propagated_on( self, mock_ssh_module, _): # Verify that keyword arguments which are specific to deploy_node() # are not propagated to create_node() mock_ssh_module.have_paramiko = True self.driver.create_node = Mock() self.driver.create_node.return_value = self.node self.driver._connect_and_run_deployment_script = Mock() self.driver._wait_until_running = Mock() kwargs = {} for key in DEPLOY_NODE_KWARGS: kwargs[key] = key kwargs["ssh_interface"] = "public_ips" kwargs["ssh_alternate_usernames"] = ["foo", "bar"] kwargs["timeout"] = 10 auth = NodeAuthPassword("P@$$w0rd") node = self.driver.deploy_node( name="name", image="image", size="size", auth=auth, ex_foo="ex_foo", **kwargs, ) self.assertEqual(self.node.id, node.id) self.assertEqual(self.driver.create_node.call_count, 1) call_kwargs = self.driver.create_node.call_args_list[0][1] expected_call_kwargs = { "name": "name", "image": "image", "size": "size", "auth": auth, "ex_foo": "ex_foo", } self.assertEqual(expected_call_kwargs, call_kwargs) # If driver throws an exception it should fall back to passing in all # 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 call_count += 1 if call_count == 1: msg = "create_node() takes at least 5 arguments (7 given)" raise TypeError(msg) return self.node self.driver.create_node = create_node node = self.driver.deploy_node( name="name", image="image", size="size", auth=auth, ex_foo="ex_foo", ex_custom_arg_1="a", ex_custom_arg_2="b", **kwargs, ) self.assertEqual(self.node.id, node.id) self.assertEqual(call_count, 2) 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 call_count += 1 if call_count == 1: msg = "create_node() missing 3 required positional arguments" raise TypeError(msg) return self.node self.driver.create_node = create_node node = self.driver.deploy_node( name="name", image="image", size="size", auth=auth, ex_foo="ex_foo", ex_custom_arg_1="a", ex_custom_arg_2="b", **kwargs, ) self.assertEqual(self.node.id, node.id) self.assertEqual(call_count, 2) @patch("libcloud.compute.base.SSHClient") @patch("libcloud.compute.ssh") def test_deploy_node_exception_run_deployment_script( self, mock_ssh_module, _): self.driver.create_node = Mock() self.driver.create_node.return_value = self.node mock_ssh_module.have_paramiko = True deploy = Mock() deploy.run = Mock() deploy.run.side_effect = Exception("foo") try: self.driver.deploy_node(deploy=deploy) except DeploymentError as e: self.assertTrue(e.node.id, self.node.id) else: self.fail("Exception was not thrown") @patch("libcloud.compute.base.SSHClient") @patch("libcloud.compute.ssh") def test_deploy_node_exception_ssh_client_connect(self, mock_ssh_module, ssh_client): self.driver.create_node = Mock() self.driver.create_node.return_value = self.node mock_ssh_module.have_paramiko = True deploy = Mock() ssh_client.side_effect = IOError("bar") try: self.driver.deploy_node(deploy=deploy) except DeploymentError as e: self.assertTrue(e.node.id, self.node.id) else: self.fail("Exception was not thrown") @patch("libcloud.compute.ssh") def test_deploy_node_depoy_node_not_implemented(self, mock_ssh_module): self.driver.features = {"create_node": []} mock_ssh_module.have_paramiko = True try: self.driver.deploy_node(deploy=Mock()) except NotImplementedError: pass else: self.fail("Exception was not thrown") self.driver.features = {} try: self.driver.deploy_node(deploy=Mock()) except NotImplementedError: pass else: self.fail("Exception was not thrown") @patch("libcloud.compute.base.SSHClient") @patch("libcloud.compute.ssh") def test_deploy_node_password_auth(self, mock_ssh_module, _): self.driver.features = {"create_node": ["password"]} mock_ssh_module.have_paramiko = True self.driver.create_node = Mock() self.driver.create_node.return_value = self.node node = self.driver.deploy_node(deploy=Mock()) self.assertEqual(self.node.id, node.id) @patch("libcloud.compute.base.SSHClient") @patch("libcloud.compute.ssh") def test_exception_is_thrown_is_paramiko_is_not_available( self, mock_ssh_module, _): self.driver.features = {"create_node": ["password"]} self.driver.create_node = Mock() self.driver.create_node.return_value = self.node mock_ssh_module.have_paramiko = False try: self.driver.deploy_node(deploy=Mock()) except RuntimeError as e: self.assertTrue(str(e).find("paramiko is not installed") != -1) else: self.fail("Exception was not thrown") mock_ssh_module.have_paramiko = True node = self.driver.deploy_node(deploy=Mock()) self.assertEqual(self.node.id, node.id)