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

Remove use of distutils #976

Merged
merged 1 commit into from Jan 31, 2022
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
4 changes: 2 additions & 2 deletions ansible_runner/config/runner.py
Expand Up @@ -23,7 +23,7 @@
import stat
import tempfile
import six
import distutils.dir_util
import shutil

from six import string_types, text_type

Expand Down Expand Up @@ -146,7 +146,7 @@ def prepare(self):
if os.path.exists(self.project_dir):
output.debug("Copying directory tree from {} to {} for working directory isolation".format(self.project_dir,
self.directory_isolation_path))
distutils.dir_util.copy_tree(self.project_dir, self.directory_isolation_path, preserve_symlinks=True)
shutil.copytree(self.project_dir, self.directory_isolation_path, symlinks=True)

self.prepare_inventory()
self.prepare_command()
Expand Down
4 changes: 1 addition & 3 deletions ansible_runner/utils/__init__.py
Expand Up @@ -17,8 +17,6 @@
import atexit
import signal

from distutils.spawn import find_executable

from ansible_runner.exceptions import ConfigurationError

try:
Expand Down Expand Up @@ -474,7 +472,7 @@ def sanitize_json_response(data):


def get_executable_path(name):
exec_path = find_executable(name)
exec_path = shutil.which(name)
if exec_path is None:
raise ConfigurationError(f"{name} command not found")
return exec_path
Expand Down
17 changes: 0 additions & 17 deletions test/conftest.py
@@ -1,6 +1,5 @@
import shutil

from distutils.version import LooseVersion
from pathlib import Path

from ansible_runner import defaults
Expand All @@ -25,16 +24,6 @@ def change_save_path(tmp_path, mocker):
mocker.patch.object(defaults, 'AUTO_CREATE_DIR', str(tmp_path))


@pytest.fixture(scope='session')
def is_pre_ansible28():
try:
if LooseVersion(pkg_resources.get_distribution('ansible').version) < LooseVersion('2.8'):
return True
except pkg_resources.DistributionNotFound:
# ansible-base (e.g. ansible 2.10 and beyond) is not accessible in this way
pass


@pytest.fixture(scope='session')
def is_pre_ansible211():
"""
Expand All @@ -51,12 +40,6 @@ def is_pre_ansible211():
return True


@pytest.fixture(scope='session')
def skipif_pre_ansible28(is_pre_ansible28):
if is_pre_ansible28:
pytest.skip("Valid only on Ansible 2.8+")


@pytest.fixture(scope='session')
def skipif_pre_ansible211(is_pre_ansible211):
if is_pre_ansible211:
Expand Down
4 changes: 2 additions & 2 deletions test/integration/test___main__.py
Expand Up @@ -98,7 +98,7 @@ def test_cmdline_role_with_playbook_option():
assert exc == 1


def test_cmdline_playbook(is_pre_ansible28, tmp_path):
def test_cmdline_playbook(tmp_path):
private_data_dir = tmp_path
play = [{'hosts': 'all', 'tasks': [{'debug': {'msg': random_string()}}]}]

Expand Down Expand Up @@ -142,7 +142,7 @@ def test_cmdline_includes_one_option():
assert exc == 1


def test_cmdline_cmdline_override(is_pre_ansible28, tmp_path):
def test_cmdline_cmdline_override(tmp_path):
private_data_dir = tmp_path
play = [{'hosts': 'all', 'tasks': [{'debug': {'msg': random_string()}}]}]

Expand Down
14 changes: 7 additions & 7 deletions test/integration/test_display_callback.py
Expand Up @@ -13,7 +13,7 @@


@pytest.fixture()
def executor(tmp_path, request, is_pre_ansible28):
def executor(tmp_path, request):
private_data_dir = tmp_path / 'foo'
private_data_dir.mkdir()

Expand Down Expand Up @@ -176,7 +176,7 @@ def test_callback_plugin_no_log_filters(executor, playbook):
- uri: url=https://example.org url_username="PUBLIC" url_password="PRIVATE"
'''}, # noqa
])
def test_callback_plugin_task_args_leak(executor, playbook, skipif_pre_ansible28):
def test_callback_plugin_task_args_leak(executor, playbook):
executor.run()
events = list(executor.events)
assert events[0]['event'] == 'playbook_on_start'
Expand Down Expand Up @@ -213,7 +213,7 @@ def test_callback_plugin_task_args_leak(executor, playbook, skipif_pre_ansible28
- debug: msg="{{ command_register.results|map(attribute='stdout')|list }}"
'''}, # noqa
])
def test_callback_plugin_censoring_does_not_overwrite(executor, playbook, skipif_pre_ansible28):
def test_callback_plugin_censoring_does_not_overwrite(executor, playbook):
executor.run()
events = list(executor.events)
assert events[0]['event'] == 'playbook_on_start'
Expand Down Expand Up @@ -258,7 +258,7 @@ def test_callback_plugin_strips_task_environ_variables(executor, playbook):
foo: "bar"
'''}, # noqa
])
def test_callback_plugin_saves_custom_stats(executor, playbook, skipif_pre_ansible28):
def test_callback_plugin_saves_custom_stats(executor, playbook):
executor.run()
for event in executor.events:
event_data = event.get('event_data', {})
Expand All @@ -284,7 +284,7 @@ def test_callback_plugin_saves_custom_stats(executor, playbook, skipif_pre_ansib
- my_handler
'''}, # noqa
])
def test_callback_plugin_records_notify_events(executor, playbook, skipif_pre_ansible28):
def test_callback_plugin_records_notify_events(executor, playbook):
executor.run()
assert len(list(executor.events))
notify_events = [x for x in executor.events if x['event'] == 'playbook_on_notify']
Expand All @@ -308,7 +308,7 @@ def test_callback_plugin_records_notify_events(executor, playbook, skipif_pre_an
url_password: "{{ pw }}"
'''}, # noqa
])
def test_module_level_no_log(executor, playbook, skipif_pre_ansible28):
def test_module_level_no_log(executor, playbook):
# It's possible for `no_log=True` to be defined at the _module_ level,
# e.g., for the URI module password parameter
# This test ensures that we properly redact those
Expand Down Expand Up @@ -398,7 +398,7 @@ def test_output_when_given_non_playbook_script(tmp_path):
msg: "{{ ('F' * 150) | list }}"
'''}, # noqa
])
def test_large_stdout_parsing_when_using_json_output(executor, playbook, skipif_pre_ansible28):
def test_large_stdout_parsing_when_using_json_output(executor, playbook):
# When the json flag is used, it is possible to output more data than
# pexpect's maxread default of 2000 characters. As a result, if not
# handled properly, the stdout can end up being corrupted with partial
Expand Down
25 changes: 11 additions & 14 deletions test/integration/test_events.py
@@ -1,16 +1,15 @@
import os
import json

from distutils.spawn import find_executable

import shutil
import pytest

from ansible_runner import defaults, run, run_async


@pytest.mark.test_all_runtimes
@pytest.mark.parametrize('containerized', [True, False])
def test_basic_events(containerized, is_pre_ansible28, runtime, tmp_path, is_run_async=False, g_facts=False):
def test_basic_events(containerized, runtime, tmp_path, is_run_async=False, g_facts=False):

inventory = 'localhost ansible_connection=local ansible_python_interpreter="{{ ansible_playbook_python }}"'

Expand Down Expand Up @@ -56,11 +55,11 @@ def test_basic_events(containerized, is_pre_ansible28, runtime, tmp_path, is_run

@pytest.mark.test_all_runtimes
@pytest.mark.parametrize('containerized', [True, False])
def test_async_events(containerized, is_pre_ansible28, runtime, tmp_path):
test_basic_events(containerized, is_pre_ansible28, runtime, tmp_path, is_run_async=True, g_facts=True)
def test_async_events(containerized, runtime, tmp_path):
test_basic_events(containerized, runtime, tmp_path, is_run_async=True, g_facts=True)


def test_basic_serializeable(is_pre_ansible28, tmp_path):
def test_basic_serializeable(tmp_path):
inv = 'localhost ansible_connection=local ansible_python_interpreter="{{ ansible_playbook_python }}"'
r = run(private_data_dir=str(tmp_path),
inventory=inv,
Expand All @@ -69,7 +68,7 @@ def test_basic_serializeable(is_pre_ansible28, tmp_path):
json.dumps(events)


def test_event_omission(is_pre_ansible28, tmp_path):
def test_event_omission(tmp_path):
inv = 'localhost ansible_connection=local ansible_python_interpreter="{{ ansible_playbook_python }}"'
r = run(private_data_dir=str(tmp_path),
inventory=inv,
Expand All @@ -86,7 +85,7 @@ def test_event_omission(is_pre_ansible28, tmp_path):
assert not any([x['event_data'] for x in events])


def test_event_omission_except_failed(is_pre_ansible28, tmp_path):
def test_event_omission_except_failed(tmp_path):
inv = 'localhost ansible_connection=local ansible_python_interpreter="{{ ansible_playbook_python }}"'
r = run(private_data_dir=str(tmp_path),
inventory=inv,
Expand All @@ -105,7 +104,7 @@ def test_event_omission_except_failed(is_pre_ansible28, tmp_path):
assert len(all_event_datas) == 1


def test_runner_on_start(rc, skipif_pre_ansible28, tmp_path):
def test_runner_on_start(rc, tmp_path):
r = run(private_data_dir=str(tmp_path),
inventory='localhost ansible_connection=local ansible_python_interpreter="{{ ansible_playbook_python }}"',
playbook=[{'hosts': 'all', 'gather_facts': False, 'tasks': [{'debug': {'msg': "test"}}]}])
Expand All @@ -114,9 +113,7 @@ def test_runner_on_start(rc, skipif_pre_ansible28, tmp_path):
assert len(start_events) == 1


def test_playbook_on_stats_summary_fields(project_fixtures, is_pre_ansible28):
if is_pre_ansible28:
pytest.skip('Test is for post 2.8 status types.')
def test_playbook_on_stats_summary_fields(project_fixtures):
private_data_dir = project_fixtures / 'host_status'

res = run(
Expand Down Expand Up @@ -147,9 +144,9 @@ def test_include_role_events(project_fixtures):
assert event_data['res']['msg'] == 'Hello world!'


@pytest.mark.skipif(find_executable('cgexec') is None,
@pytest.mark.skipif(shutil.which('cgexec') is None,
reason="cgexec not available")
def test_profile_data(skipif_pre_ansible28, tmp_path):
def test_profile_data(tmp_path):
try:
r = run(private_data_dir=str(tmp_path),
inventory='localhost ansible_connection=local ansible_python_interpreter="{{ ansible_playbook_python }}"',
Expand Down
4 changes: 2 additions & 2 deletions test/unit/config/test_runner.py
Expand Up @@ -223,7 +223,7 @@ def test_prepare_env_directory_isolation_from_settings(mocker, project_fixtures)
'''
# Mock away the things that would actually prepare the isolation directory.
mocker.patch('os.makedirs', return_value=True)
copy_tree = mocker.patch('distutils.dir_util.copy_tree')
copy_tree = mocker.patch('shutil.copytree')
mkdtemp = mocker.patch('tempfile.mkdtemp')
mkdtemp.return_value = '/tmp/runner/runner_di_XYZ'
mocker.patch('ansible_runner.config.runner.RunnerConfig.build_process_isolation_temp_dir')
Expand All @@ -245,7 +245,7 @@ def test_prepare_env_directory_isolation_from_settings(mocker, project_fixtures)
mkdtemp.assert_called_once_with(prefix='runner_di_', dir='/tmp/runner')

# The project files should be copied to the isolation path.
copy_tree.assert_called_once_with(rc.project_dir, rc.directory_isolation_path, preserve_symlinks=True)
copy_tree.assert_called_once_with(rc.project_dir, rc.directory_isolation_path, symlinks=True)


def test_prepare_inventory(mocker):
Expand Down