[stable-2.7] Skip tests with unsatisfied deps (#55853)

* Skip gitlab tests if dependencies aren't met

* Skip certain unittests if passlib is not installed

* Fix tests with deps on paramiko to skip if paramiko is not installed

* Use pytest to skip for cloudstack

If either on Python-2.6 or the cs library is not installed we cannot run
this test so skip it.
(cherry picked from commit 8acf71f78f)

Co-authored-by: Toshio Kuratomi <a.badger@gmail.com>
pull/58677/head
Toshio Kuratomi 5 years ago
parent 28816088e4
commit 7d4e9b279e

@ -23,7 +23,9 @@ import sys
import pytest
from ansible.compat.tests import unittest
from ansible.plugins.filter.network import parse_xml, type5_pw, hash_salt, comp_type5
from ansible.plugins.filter.network import (HAS_PASSLIB, parse_xml, type5_pw, hash_salt,
comp_type5)
from ansible.errors import AnsibleFilterError
fixture_path = os.path.join(os.path.dirname(__file__), 'fixtures', 'network')
@ -83,6 +85,7 @@ class TestNetworkParseFilter(unittest.TestCase):
self.assertEqual("parse_xml works on string input, but given input of : %s" % type(output), str(e.exception))
@pytest.mark.skipif(not HAS_PASSLIB, reason="Current type5_pw filter needs passlib to function")
class TestNetworkType5(unittest.TestCase):
def test_defined_salt_success(self):
@ -146,6 +149,7 @@ class TestHashSalt(unittest.TestCase):
self.assertEqual("Could not parse salt out password correctly from $nTc1$Z28sUTcWfXlvVe2x.3XAa.", str(e.exception))
@pytest.mark.skipif(not HAS_PASSLIB, reason="Current comp_type5 filter needs passlib to function")
class TestCompareType5(unittest.TestCase):
def test_compare_type5_boolean(self):
@ -160,7 +164,7 @@ class TestCompareType5(unittest.TestCase):
parsed = comp_type5(unencrypted_password, encrypted_password, True)
self.assertEqual(parsed, '$1$nTc1$Z28sUTcWfXlvVe2x.3XAa.')
def test_compate_type5_fail(self):
def test_compare_type5_fail(self):
unencrypted_password = 'invalid_password'
encrypted_password = '$1$nTc1$Z28sUTcWfXlvVe2x.3XAa.'
parsed = comp_type5(unencrypted_password, encrypted_password)

@ -20,8 +20,15 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import passlib
from passlib.handlers import pbkdf2
try:
import passlib
from passlib.handlers import pbkdf2
except ImportError:
passlib = None
pbkdf2 = None
import pytest
from units.mock.loader import DictDataLoader
from ansible.compat.tests import unittest
@ -359,7 +366,7 @@ class TestWritePasswordFile(unittest.TestCase):
m().write.assert_called_once_with(u'Testing Café\n'.encode('utf-8'))
class TestLookupModule(unittest.TestCase):
class BaseTestLookupModule(unittest.TestCase):
def setUp(self):
self.fake_loader = DictDataLoader({'/path/to/somewhere': 'sdfsdf'})
self.password_lookup = password.LookupModule(loader=self.fake_loader)
@ -373,19 +380,15 @@ class TestLookupModule(unittest.TestCase):
self.makedirs_safe = password.makedirs_safe
password.makedirs_safe = lambda path, mode: None
# Different releases of passlib default to a different number of rounds
self.sha256 = passlib.registry.get_crypt_handler('pbkdf2_sha256')
sha256_for_tests = pbkdf2.create_pbkdf2_hash("sha256", 32, 20000)
passlib.registry.register_crypt_handler(sha256_for_tests, force=True)
def tearDown(self):
password.os.path.exists = self.os_path_exists
password.os.open = self.os_open
password.os.close = self.os_close
password.os.remove = self.os_remove
password.makedirs_safe = self.makedirs_safe
passlib.registry.register_crypt_handler(self.sha256, force=True)
class TestLookupModuleWithoutPasslib(BaseTestLookupModule):
@patch.object(PluginLoader, '_get_paths')
@patch('ansible.plugins.lookup.password._write_password_file')
def test_no_encrypt(self, mock_get_paths, mock_write_file):
@ -395,46 +398,8 @@ class TestLookupModule(unittest.TestCase):
# FIXME: assert something useful
for result in results:
self.assertEquals(len(result), password.DEFAULT_LENGTH)
self.assertIsInstance(result, text_type)
@patch.object(PluginLoader, '_get_paths')
@patch('ansible.plugins.lookup.password._write_password_file')
def test_encrypt(self, mock_get_paths, mock_write_file):
mock_get_paths.return_value = ['/path/one', '/path/two', '/path/three']
results = self.password_lookup.run([u'/path/to/somewhere encrypt=pbkdf2_sha256'], None)
# pbkdf2 format plus hash
expected_password_length = 76
for result in results:
self.assertEquals(len(result), expected_password_length)
# result should have 5 parts split by '$'
str_parts = result.split('$', 5)
# verify the result is parseable by the passlib
crypt_parts = passlib.hash.pbkdf2_sha256.parsehash(result)
# verify it used the right algo type
self.assertEquals(str_parts[1], 'pbkdf2-sha256')
self.assertEquals(len(str_parts), 5)
# verify the string and parsehash agree on the number of rounds
self.assertEquals(int(str_parts[2]), crypt_parts['rounds'])
self.assertIsInstance(result, text_type)
@patch.object(PluginLoader, '_get_paths')
@patch('ansible.plugins.lookup.password._write_password_file')
def test_password_already_created_encrypt(self, mock_get_paths, mock_write_file):
mock_get_paths.return_value = ['/path/one', '/path/two', '/path/three']
password.os.path.exists = lambda x: x == to_bytes('/path/to/somewhere')
with patch.object(builtins, 'open', mock_open(read_data=b'hunter42 salt=87654321\n')) as m:
results = self.password_lookup.run([u'/path/to/somewhere chars=anything encrypt=pbkdf2_sha256'], None)
for result in results:
self.assertEqual(result, u'$pbkdf2-sha256$20000$ODc2NTQzMjE$Uikde0cv0BKaRaAXMrUQB.zvG4GmnjClwjghwIRf2gU')
assert len(result) == password.DEFAULT_LENGTH
assert isinstance(result, text_type)
@patch.object(PluginLoader, '_get_paths')
@patch('ansible.plugins.lookup.password._write_password_file')
@ -480,3 +445,57 @@ class TestLookupModule(unittest.TestCase):
for result in results:
self.assertEqual(result, u'hunter42')
@pytest.mark.skipif(passlib is None, reason='passlib must be installed to run these tests')
class TestLookupModuleWithPasslib(BaseTestLookupModule):
def setUp(self):
super(TestLookupModuleWithPasslib, self).setUp()
# Different releases of passlib default to a different number of rounds
self.sha256 = passlib.registry.get_crypt_handler('pbkdf2_sha256')
sha256_for_tests = pbkdf2.create_pbkdf2_hash("sha256", 32, 20000)
passlib.registry.register_crypt_handler(sha256_for_tests, force=True)
def tearDown(self):
super(TestLookupModuleWithPasslib, self).tearDown()
passlib.registry.register_crypt_handler(self.sha256, force=True)
@patch.object(PluginLoader, '_get_paths')
@patch('ansible.plugins.lookup.password._write_password_file')
def test_encrypt(self, mock_get_paths, mock_write_file):
mock_get_paths.return_value = ['/path/one', '/path/two', '/path/three']
results = self.password_lookup.run([u'/path/to/somewhere encrypt=pbkdf2_sha256'], None)
# pbkdf2 format plus hash
expected_password_length = 76
for result in results:
self.assertEquals(len(result), expected_password_length)
# result should have 5 parts split by '$'
str_parts = result.split('$', 5)
# verify the result is parseable by the passlib
crypt_parts = passlib.hash.pbkdf2_sha256.parsehash(result)
# verify it used the right algo type
self.assertEquals(str_parts[1], 'pbkdf2-sha256')
self.assertEquals(len(str_parts), 5)
# verify the string and parsehash agree on the number of rounds
self.assertEquals(int(str_parts[2]), crypt_parts['rounds'])
self.assertIsInstance(result, text_type)
@patch.object(PluginLoader, '_get_paths')
@patch('ansible.plugins.lookup.password._write_password_file')
def test_password_already_created_encrypt(self, mock_get_paths, mock_write_file):
mock_get_paths.return_value = ['/path/one', '/path/two', '/path/three']
password.os.path.exists = lambda x: x == to_bytes('/path/to/somewhere')
with patch.object(builtins, 'open', mock_open(read_data=b'hunter42 salt=87654321\n')) as m:
results = self.password_lookup.run([u'/path/to/somewhere chars=anything encrypt=pbkdf2_sha256'], None)
for result in results:
self.assertEqual(result, u'$pbkdf2-sha256$20000$ODc2NTQzMjE$Uikde0cv0BKaRaAXMrUQB.zvG4GmnjClwjghwIRf2gU')

@ -36,16 +36,29 @@ class passlib_off(object):
def assert_hash(expected, secret, algorithm, **settings):
assert encrypt.CryptHash(algorithm).hash(secret, **settings) == expected
if encrypt.PASSLIB_AVAILABLE:
assert encrypt.passlib_or_crypt(secret, algorithm, **settings) == expected
assert encrypt.PasslibHash(algorithm).hash(secret, **settings) == expected
else:
with pytest.raises(AnsibleFilterError):
assert encrypt.passlib_or_crypt(secret, algorithm, **settings) == expected
with pytest.raises(AnsibleError) as excinfo:
encrypt.PasslibHash(algorithm).hash(secret, **settings)
assert excinfo.value.args[0] == "passlib must be installed to hash with '%s'" % algorithm
def test_encrypt_with_rounds_no_passlib():
with passlib_off():
assert_hash("$5$12345678$uAZsE3BenI2G.nA8DpTl.9Dc8JiqacI53pEqRr5ppT7",
secret="123", algorithm="sha256_crypt", salt="12345678", rounds=5000)
assert_hash("$5$rounds=10000$12345678$JBinliYMFEcBeAXKZnLjenhgEhTmJBvZn3aR8l70Oy/",
secret="123", algorithm="sha256_crypt", salt="12345678", rounds=10000)
assert_hash("$6$12345678$LcV9LQiaPekQxZ.OfkMADjFdSO2k9zfbDQrHPVcYjSLqSdjLYpsgqviYvTEP/R41yPmhH3CCeEDqVhW1VHr3L.",
secret="123", algorithm="sha512_crypt", salt="12345678", rounds=5000)
# If passlib is not installed. this is identical to the test_encrypt_with_rounds_no_passlib() test
@pytest.mark.skipif(not encrypt.PASSLIB_AVAILABLE, reason='passlib must be installed to run this test')
def test_encrypt_with_rounds():
assert_hash("$5$12345678$uAZsE3BenI2G.nA8DpTl.9Dc8JiqacI53pEqRr5ppT7",
secret="123", algorithm="sha256_crypt", salt="12345678", rounds=5000)
@ -55,6 +68,20 @@ def test_encrypt_with_rounds():
secret="123", algorithm="sha512_crypt", salt="12345678", rounds=5000)
def test_encrypt_default_rounds_no_passlib():
with passlib_off():
assert_hash("$1$12345678$tRy4cXc3kmcfRZVj4iFXr/",
secret="123", algorithm="md5_crypt", salt="12345678")
assert_hash("$5$12345678$uAZsE3BenI2G.nA8DpTl.9Dc8JiqacI53pEqRr5ppT7",
secret="123", algorithm="sha256_crypt", salt="12345678")
assert_hash("$6$12345678$LcV9LQiaPekQxZ.OfkMADjFdSO2k9zfbDQrHPVcYjSLqSdjLYpsgqviYvTEP/R41yPmhH3CCeEDqVhW1VHr3L.",
secret="123", algorithm="sha512_crypt", salt="12345678")
assert encrypt.CryptHash("md5_crypt").hash("123")
# If passlib is not installed. this is identical to the test_encrypt_default_rounds_no_passlib() test
@pytest.mark.skipif(not encrypt.PASSLIB_AVAILABLE, reason='passlib must be installed to run this test')
def test_encrypt_default_rounds():
assert_hash("$1$12345678$tRy4cXc3kmcfRZVj4iFXr/",
secret="123", algorithm="md5_crypt", salt="12345678")

Loading…
Cancel
Save