Skip to content

Commit

Permalink
Fix cryptography constraints in ansible-test. (ansible#72914)
Browse files Browse the repository at this point in the history
  • Loading branch information
mattclay authored Dec 9, 2020
1 parent 83764ad commit 36ab3d1
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
bugfixes:
- ansible-test - ``cryptography`` is now limited to versions prior to 3.2 only when an incompatible OpenSSL version (earlier than 1.1.0) is detected
1 change: 1 addition & 0 deletions test/lib/ansible_test/_data/cryptography-constraints.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
# do not add a cryptography constraint here, see the get_cryptography_requirement function in executor.py for details
idna < 2.8 ; python_version < '2.7' # idna 2.8+ requires python 2.7+
cffi != 1.14.4 # Fails on systems with older gcc. Should be fixed in the next release. https://foss.heptapod.net/pypy/cffi/-/issues/480
2 changes: 1 addition & 1 deletion test/lib/ansible_test/_data/requirements/constraints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ coverage >= 4.5.1, < 5.0.0 ; python_version < '3.7' # coverage 4.4 required for
coverage >= 4.5.2, < 5.0.0 ; python_version == '3.7' # coverage 4.5.2 fixes bugs in support for python 3.7, coverage 5.0+ incompatible
coverage >= 4.5.4, < 5.0.0 ; python_version > '3.7' # coverage had a bug in < 4.5.4 that would cause unit tests to hang in Python 3.8, coverage 5.0+ incompatible
cryptography < 2.2 ; python_version < '2.7' # cryptography 2.2 drops support for python 2.6
cryptography < 3.2 ; python_version >= '2.7' # cryptography 3.2 drops support for OpenSSL 1.0.2 which some of our CI hosts (FreeBSD) still use
# do not add a cryptography constraint here unless it is for python version incompatibility, see the get_cryptography_requirement function in executor.py for details
deepdiff < 4.0.0 ; python_version < '3' # deepdiff 4.0.0 and later require python 3
jinja2 < 2.11 ; python_version < '2.7' # jinja2 2.11 and later require python 2.7 or later
urllib3 < 1.24 ; python_version < '2.7' # urllib3 1.24 and later require python 2.7 or later
Expand Down
24 changes: 24 additions & 0 deletions test/lib/ansible_test/_data/sslcheck.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/usr/bin/env python
"""Show openssl version."""
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type

import json

# noinspection PyBroadException
try:
from ssl import OPENSSL_VERSION_INFO
VERSION = list(OPENSSL_VERSION_INFO[:3])
except Exception: # pylint: disable=broad-except
VERSION = None


def main():
"""Main program entry point."""
print(json.dumps(dict(
version=VERSION,
)))


if __name__ == '__main__':
main()
44 changes: 43 additions & 1 deletion test/lib/ansible_test/_internal/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
open_zipfile,
SUPPORTED_PYTHON_VERSIONS,
str_to_version,
version_to_str,
)

from .util_common import (
Expand Down Expand Up @@ -185,6 +186,42 @@ def create_shell_command(command):
return cmd


def get_openssl_version(args, python, python_version): # type: (EnvironmentConfig, str, str) -> t.Optional[t.Tuple[int, ...]]
"""Return the openssl version."""
if not python_version.startswith('2.'):
# OpenSSL version checking only works on Python 3.x.
# This should be the most accurate, since it is the Python we will be using.
version = json.loads(run_command(args, [python, os.path.join(ANSIBLE_TEST_DATA_ROOT, 'sslcheck.py')], capture=True, always=True)[0])['version']

if version:
display.info('Detected OpenSSL version %s under Python %s.' % (version_to_str(version), python_version), verbosity=1)

return tuple(version)

# Fall back to detecting the OpenSSL version from the CLI.
# This should provide an adequate solution on Python 2.x.
openssl_path = find_executable('openssl', required=False)

if openssl_path:
try:
result = raw_command([openssl_path, 'version'], capture=True)[0]
except SubprocessError:
result = ''

match = re.search(r'^OpenSSL (?P<version>[0-9]+\.[0-9]+\.[0-9]+)', result)

if match:
version = str_to_version(match.group('version'))

display.info('Detected OpenSSL version %s using the openssl CLI.' % version_to_str(version), verbosity=1)

return version

display.info('Unable to detect OpenSSL version.', verbosity=1)

return None


def get_setuptools_version(args, python): # type: (EnvironmentConfig, str) -> t.Tuple[int]
"""Return the setuptools version for the given python."""
try:
Expand All @@ -199,16 +236,21 @@ def get_setuptools_version(args, python): # type: (EnvironmentConfig, str) -> t
def get_cryptography_requirement(args, python_version): # type: (EnvironmentConfig, str) -> str
"""
Return the correct cryptography requirement for the given python version.
The version of cryptograpy installed depends on the python version and setuptools version.
The version of cryptography installed depends on the python version, setuptools version and openssl version.
"""
python = find_python(python_version)
setuptools_version = get_setuptools_version(args, python)
openssl_version = get_openssl_version(args, python, python_version)

if setuptools_version >= (18, 5):
if python_version == '2.6':
# cryptography 2.2+ requires python 2.7+
# see https://github.com/pyca/cryptography/blob/master/CHANGELOG.rst#22---2018-03-19
cryptography = 'cryptography < 2.2'
elif openssl_version and openssl_version < (1, 1, 0):
# cryptography 3.2 requires openssl 1.1.x or later
# see https://cryptography.io/en/latest/changelog.html#v3-2
cryptography = 'cryptography < 3.2'
else:
cryptography = 'cryptography'
else:
Expand Down
4 changes: 2 additions & 2 deletions test/lib/ansible_test/_internal/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -771,12 +771,12 @@ def paths_to_dirs(paths): # type: (t.List[str]) -> t.List[str]
return sorted(dir_names)


def str_to_version(version): # type: (str) -> t.Tuple[int]
def str_to_version(version): # type: (str) -> t.Tuple[int, ...]
"""Return a version tuple from a version string."""
return tuple(int(n) for n in version.split('.'))


def version_to_str(version): # type: (t.Tuple[int]) -> str
def version_to_str(version): # type: (t.Tuple[int, ...]) -> str
"""Return a version string from a version tuple."""
return '.'.join(str(n) for n in version)

Expand Down

0 comments on commit 36ab3d1

Please sign in to comment.