summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatt Clay <mclay@redhat.com>2022-02-02 18:05:26 -0800
committerGitHub <noreply@github.com>2022-02-02 18:05:26 -0800
commit62d60ad1bf13bf897719d8221f0f6ba046b6db6d (patch)
tree1f612e4c2ee7fe541b64d175f32dfe6965070c76
parent82b1731280bd6f4b7d092b44c9ad39b01c78ae45 (diff)
downloadansible-62d60ad1bf13bf897719d8221f0f6ba046b6db6d.tar.gz
[stable-2.9] ansible-test - Use relative paths in junit output. (#76911)
* [stable-2.9] ansible-test - Use relative paths in junit output. (#76871) * ansible-test - Use relative paths in junit output. * ansible-test - Handle out-of-tree JUnit paths. * Also fix a traceback in the junit callback during automatic fact gathering. (cherry picked from commit fbb5d56bd274c44b193cb95f0230b9352f62aab2). * Fix task path unicode error in junit callback. (cherry picked from commit 41db6d8d35900d425df3228406db3fec61ab2269)
-rw-r--r--changelogs/fragments/ansible-test-junit-relative-paths.yml6
-rw-r--r--changelogs/fragments/junit-callback-task-path-unicode.yml2
-rw-r--r--lib/ansible/plugins/callback/junit.py18
-rw-r--r--test/lib/ansible_test/_internal/executor.py10
-rw-r--r--test/lib/ansible_test/_internal/integration/__init__.py7
5 files changed, 35 insertions, 8 deletions
diff --git a/changelogs/fragments/ansible-test-junit-relative-paths.yml b/changelogs/fragments/ansible-test-junit-relative-paths.yml
new file mode 100644
index 0000000000..b1ace19687
--- /dev/null
+++ b/changelogs/fragments/ansible-test-junit-relative-paths.yml
@@ -0,0 +1,6 @@
+bugfixes:
+ - ansible-test - Use relative paths in JUnit files generated during integration test runs.
+ - ansible-test - Replace the directory portion of out-of-tree paths in JUnit files from integration tests with the ``out-of-tree:`` prefix.
+ - junit callback - Fix traceback during automatic fact gathering when using relative paths.
+minor_changes:
+ - junit callback - Add support for replacing the directory portion of out-of-tree relative task paths with a placeholder.
diff --git a/changelogs/fragments/junit-callback-task-path-unicode.yml b/changelogs/fragments/junit-callback-task-path-unicode.yml
new file mode 100644
index 0000000000..2b1ffd41ea
--- /dev/null
+++ b/changelogs/fragments/junit-callback-task-path-unicode.yml
@@ -0,0 +1,2 @@
+bugfixes:
+ - junit callback - Fix unicode error when handling non-ASCII task paths.
diff --git a/lib/ansible/plugins/callback/junit.py b/lib/ansible/plugins/callback/junit.py
index 2fe402ea2f..838f491def 100644
--- a/lib/ansible/plugins/callback/junit.py
+++ b/lib/ansible/plugins/callback/junit.py
@@ -40,6 +40,13 @@ DOCUMENTATION = '''
version_added: "2.8"
env:
- name: JUNIT_TASK_RELATIVE_PATH
+ replace_out_of_tree_path:
+ name: Replace out of tree path
+ default: none
+ description: Replace the directory portion of an out-of-tree relative task path with the given placeholder
+ version_added: "2.12.3"
+ env:
+ - name: JUNIT_REPLACE_OUT_OF_TREE_PATH
fail_on_change:
name: JUnit fail on change
default: False
@@ -155,6 +162,7 @@ class CallbackModule(CallbackBase):
self._include_setup_tasks_in_report = os.getenv('JUNIT_INCLUDE_SETUP_TASKS_IN_REPORT', 'True').lower()
self._hide_task_arguments = os.getenv('JUNIT_HIDE_TASK_ARGUMENTS', 'False').lower()
self._test_case_prefix = os.getenv('JUNIT_TEST_CASE_PREFIX', '')
+ self._replace_out_of_tree_path = os.getenv('JUNIT_REPLACE_OUT_OF_TREE_PATH', None)
self._playbook_path = None
self._playbook_name = None
self._play_name = None
@@ -174,6 +182,9 @@ class CallbackModule(CallbackBase):
self._display.warning('The `ordereddict` python module is not installed. '
'Disabling the `junit` callback plugin.')
+ if self._replace_out_of_tree_path is not None:
+ self._replace_out_of_tree_path = to_text(self._replace_out_of_tree_path)
+
if not os.path.exists(self._output_dir):
os.makedirs(self._output_dir)
@@ -232,11 +243,14 @@ class CallbackModule(CallbackBase):
name = '[%s] %s: %s' % (host_data.name, task_data.play, task_data.name)
duration = host_data.finish - task_data.start
- if self._task_relative_path:
- junit_classname = os.path.relpath(task_data.path, self._task_relative_path)
+ if self._task_relative_path and task_data.path:
+ junit_classname = to_text(os.path.relpath(to_bytes(task_data.path), to_bytes(self._task_relative_path)))
else:
junit_classname = task_data.path
+ if self._replace_out_of_tree_path is not None and junit_classname.startswith('../'):
+ junit_classname = self._replace_out_of_tree_path + to_text(os.path.basename(to_bytes(junit_classname)))
+
if self._task_class == 'true':
junit_classname = re.sub(r'\.yml:[0-9]+$', '', junit_classname)
diff --git a/test/lib/ansible_test/_internal/executor.py b/test/lib/ansible_test/_internal/executor.py
index 26afac58ac..078ef10897 100644
--- a/test/lib/ansible_test/_internal/executor.py
+++ b/test/lib/ansible_test/_internal/executor.py
@@ -135,6 +135,7 @@ from .integration import (
get_inventory_relative_path,
check_inventory,
delegate_inventory,
+ IntegrationEnvironment,
)
from .data import (
@@ -1435,7 +1436,7 @@ def run_setup_targets(args, test_dir, target_names, targets_dict, targets_execut
targets_executed.add(target_name)
-def integration_environment(args, target, test_dir, inventory_path, ansible_config, env_config):
+def integration_environment(args, target, test_dir, inventory_path, ansible_config, env_config, test_env):
"""
:type args: IntegrationConfig
:type target: IntegrationTarget
@@ -1443,6 +1444,7 @@ def integration_environment(args, target, test_dir, inventory_path, ansible_conf
:type inventory_path: str
:type ansible_config: str | None
:type env_config: CloudEnvironmentConfig | None
+ :type test_env: IntegrationEnvironment
:rtype: dict[str, str]
"""
env = ansible_environment(args, ansible_config=ansible_config)
@@ -1456,6 +1458,8 @@ def integration_environment(args, target, test_dir, inventory_path, ansible_conf
integration = dict(
JUNIT_OUTPUT_DIR=ResultType.JUNIT.path,
+ JUNIT_TASK_RELATIVE_PATH=test_env.test_dir,
+ JUNIT_REPLACE_OUT_OF_TREE_PATH='out-of-tree:',
ANSIBLE_CALLBACK_WHITELIST=','.join(sorted(set(callback_plugins))),
ANSIBLE_TEST_CI=args.metadata.ci_provider or get_ci_provider().code,
ANSIBLE_TEST_COVERAGE='check' if args.coverage_check else ('yes' if args.coverage else ''),
@@ -1502,7 +1506,7 @@ def command_integration_script(args, target, test_dir, inventory_path, temp_path
if args.verbosity:
cmd.append('-' + ('v' * args.verbosity))
- env = integration_environment(args, target, test_dir, test_env.inventory_path, test_env.ansible_config, env_config)
+ env = integration_environment(args, target, test_dir, test_env.inventory_path, test_env.ansible_config, env_config, test_env)
cwd = os.path.join(test_env.targets_dir, target.relative_path)
env.update(dict(
@@ -1610,7 +1614,7 @@ def command_integration_role(args, target, start_at_task, test_dir, inventory_pa
if args.verbosity:
cmd.append('-' + ('v' * args.verbosity))
- env = integration_environment(args, target, test_dir, test_env.inventory_path, test_env.ansible_config, env_config)
+ env = integration_environment(args, target, test_dir, test_env.inventory_path, test_env.ansible_config, env_config, test_env)
cwd = test_env.integration_dir
env.update(dict(
diff --git a/test/lib/ansible_test/_internal/integration/__init__.py b/test/lib/ansible_test/_internal/integration/__init__.py
index 84e5e097c0..a4d146307f 100644
--- a/test/lib/ansible_test/_internal/integration/__init__.py
+++ b/test/lib/ansible_test/_internal/integration/__init__.py
@@ -206,7 +206,7 @@ def integration_test_environment(args, target, inventory_path_src):
ansible_config = ansible_config_src
vars_file = os.path.join(data_context().content.root, data_context().content.integration_vars_path)
- yield IntegrationEnvironment(integration_dir, targets_dir, inventory_path, ansible_config, vars_file)
+ yield IntegrationEnvironment(data_context().content.root, integration_dir, targets_dir, inventory_path, ansible_config, vars_file)
return
# When testing a collection, the temporary directory must reside within the collection.
@@ -284,7 +284,7 @@ def integration_test_environment(args, target, inventory_path_src):
make_dirs(os.path.dirname(file_dst))
shutil.copy2(file_src, file_dst)
- yield IntegrationEnvironment(integration_dir, targets_dir, inventory_path, ansible_config, vars_file)
+ yield IntegrationEnvironment(temp_dir, integration_dir, targets_dir, inventory_path, ansible_config, vars_file)
finally:
if not args.explain:
shutil.rmtree(temp_dir)
@@ -322,7 +322,8 @@ def integration_test_config_file(args, env_config, integration_dir):
class IntegrationEnvironment:
"""Details about the integration environment."""
- def __init__(self, integration_dir, targets_dir, inventory_path, ansible_config, vars_file):
+ def __init__(self, test_dir, integration_dir, targets_dir, inventory_path, ansible_config, vars_file):
+ self.test_dir = test_dir
self.integration_dir = integration_dir
self.targets_dir = targets_dir
self.inventory_path = inventory_path