Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix ansible-test handling of egg-info. #73594

Merged
merged 1 commit into from Feb 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 11 additions & 0 deletions changelogs/fragments/ansible-test-egg-info-handling.yml
@@ -0,0 +1,11 @@
bugfixes:
- ansible-test - Running tests using an installed version of ``ansible-test`` against one Python version from another no longer fails
due to a missing ``egg-info`` directory.
This could occur when testing plugins which import ``pkg_resources``.
- ansible-test - Running tests using an installed version of ``ansible-test`` no longer generates an error attempting to create an ``egg-info`` directory
when an existing one is not found in the expected location.
This could occur if the existing ``egg-info`` directory included a Python version specifier in the name.
minor_changes:
- ansible-test - Generation of an ``egg-info`` directory, if needed, is now done after installing test dependencies and before running tests.
When running from an installed version of ``ansible-test`` a temporary directory is used to avoid permissions issues.
Previously it was done before installing test dependencies and adjacent to the installed directory.
54 changes: 45 additions & 9 deletions test/lib/ansible_test/_internal/ansible_util.py
Expand Up @@ -11,6 +11,10 @@
SOFT_RLIMIT_NOFILE,
)

from .io import (
write_text_file,
)

from .util import (
common_environment,
display,
Expand All @@ -20,6 +24,7 @@
ANSIBLE_TEST_DATA_ROOT,
ANSIBLE_BIN_PATH,
ANSIBLE_SOURCE_ROOT,
get_ansible_version,
)

from .util_common import (
Expand Down Expand Up @@ -75,7 +80,7 @@ def ansible_environment(args, color=True, ansible_config=None):
ANSIBLE_LIBRARY='/dev/null',
ANSIBLE_DEVEL_WARNING='false', # Don't show warnings that CI is running devel
ANSIBLE_CONTROLLER_PYTHON_WARNING='false', # Don't show warnings in CI for old controller Python
PYTHONPATH=get_ansible_python_path(),
PYTHONPATH=get_ansible_python_path(args),
PAGER='/bin/cat',
PATH=path,
# give TQM worker processes time to report code coverage results
Expand Down Expand Up @@ -171,28 +176,59 @@ def configure_plugin_paths(args): # type: (CommonConfig) -> t.Dict[str, str]
return env


def get_ansible_python_path(): # type: () -> str
def get_ansible_python_path(args): # type: (CommonConfig) -> str
"""
Return a directory usable for PYTHONPATH, containing only the ansible package.
If a temporary directory is required, it will be cached for the lifetime of the process and cleaned up at exit.
"""
if ANSIBLE_SOURCE_ROOT:
# when running from source there is no need for a temporary directory to isolate the ansible package
return os.path.dirname(ANSIBLE_LIB_ROOT)

try:
return get_ansible_python_path.python_path
except AttributeError:
pass

python_path = create_temp_dir(prefix='ansible-test-')
get_ansible_python_path.python_path = python_path
if ANSIBLE_SOURCE_ROOT:
# when running from source there is no need for a temporary directory to isolate the ansible package
python_path = os.path.dirname(ANSIBLE_LIB_ROOT)
else:
# when not running from source the installed directory is unsafe to add to PYTHONPATH
# doing so would expose many unwanted packages on sys.path
# instead a temporary directory is created which contains only ansible using a symlink
python_path = create_temp_dir(prefix='ansible-test-')

os.symlink(ANSIBLE_LIB_ROOT, os.path.join(python_path, 'ansible'))

if not args.explain:
generate_egg_info(python_path)

os.symlink(ANSIBLE_LIB_ROOT, os.path.join(python_path, 'ansible'))
get_ansible_python_path.python_path = python_path

return python_path


def generate_egg_info(path): # type: (str) -> None
"""Generate an egg-info in the specified base directory."""
# minimal PKG-INFO stub following the format defined in PEP 241
# required for older setuptools versions to avoid a traceback when importing pkg_resources from packages like cryptography
# newer setuptools versions are happy with an empty directory
# including a stub here means we don't need to locate the existing file or have setup.py generate it when running from source
pkg_info = '''
Metadata-Version: 1.0
Name: ansible
Version: %s
Platform: UNKNOWN
Summary: Radically simple IT automation
Author-email: info@ansible.com
License: GPLv3+
''' % get_ansible_version()

pkg_info_path = os.path.join(path, 'ansible_core.egg-info', 'PKG-INFO')

if os.path.exists(pkg_info_path):
return

write_text_file(pkg_info_path, pkg_info.lstrip(), create_directories=True)


def check_pyyaml(args, version, required=True, quiet=False):
"""
:type args: EnvironmentConfig
Expand Down
42 changes: 0 additions & 42 deletions test/lib/ansible_test/_internal/executor.py
Expand Up @@ -298,8 +298,6 @@ def install_command_requirements(args, python_version=None, context=None, enable
if args.raw:
return

generate_egg_info(args)

if not args.requirements:
return

Expand Down Expand Up @@ -434,46 +432,6 @@ def pip_list(args, pip):
return stdout


def generate_egg_info(args):
"""
:type args: EnvironmentConfig
"""
if args.explain:
return

ansible_version = get_ansible_version()

# inclusion of the version number in the path is optional
# see: https://setuptools.readthedocs.io/en/latest/formats.html#filename-embedded-metadata
egg_info_path = ANSIBLE_LIB_ROOT + '_core-%s.egg-info' % ansible_version

if os.path.exists(egg_info_path):
return

egg_info_path = ANSIBLE_LIB_ROOT + '_core.egg-info'

if os.path.exists(egg_info_path):
return

# minimal PKG-INFO stub following the format defined in PEP 241
# required for older setuptools versions to avoid a traceback when importing pkg_resources from packages like cryptography
# newer setuptools versions are happy with an empty directory
# including a stub here means we don't need to locate the existing file or have setup.py generate it when running from source
pkg_info = '''
Metadata-Version: 1.0
Name: ansible
Version: %s
Platform: UNKNOWN
Summary: Radically simple IT automation
Author-email: info@ansible.com
License: GPLv3+
''' % get_ansible_version()

pkg_info_path = os.path.join(egg_info_path, 'PKG-INFO')

write_text_file(pkg_info_path, pkg_info.lstrip(), create_directories=True)


def generate_pip_install(pip, command, packages=None, constraints=None, use_constraints=True, context=None):
"""
:type pip: list[str]
Expand Down