ansible-test - split controller/target testing (#75605)

pull/75743/head
Matt Clay 3 years ago committed by GitHub
parent 989eeb243f
commit 4ea8d9a782
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -25,7 +25,7 @@ recursive-include packaging *
recursive-include test/ansible_test *.py Makefile recursive-include test/ansible_test *.py Makefile
recursive-include test/integration * recursive-include test/integration *
recursive-include test/lib/ansible_test/config *.yml *.template recursive-include test/lib/ansible_test/config *.yml *.template
recursive-include test/lib/ansible_test/_data *.cfg *.ini *.ps1 *.txt *.yml coveragerc inventory recursive-include test/lib/ansible_test/_data *.cfg *.ini *.ps1 *.txt *.yml coveragerc
recursive-include test/lib/ansible_test/_util *.cfg *.json *.ps1 *.psd1 *.py *.sh *.txt *.yml recursive-include test/lib/ansible_test/_util *.cfg *.json *.ps1 *.psd1 *.py *.sh *.txt *.yml
recursive-include test/lib/ansible_test/_util/target/injector ansible ansible-config ansible-connection ansible-console ansible-doc ansible-galaxy ansible-inventory ansible-playbook ansible-pull ansible-test ansible-vault pytest recursive-include test/lib/ansible_test/_util/target/injector ansible ansible-config ansible-connection ansible-console ansible-doc ansible-galaxy ansible-inventory ansible-playbook ansible-pull ansible-test ansible-vault pytest
recursive-include test/lib/ansible_test/_util/controller/sanity/validate-modules validate-modules recursive-include test/lib/ansible_test/_util/controller/sanity/validate-modules validate-modules

@ -1 +1 @@
../test/lib/ansible_test/_util/controller/cli/ansible_test_cli_stub.py ../test/lib/ansible_test/_util/target/cli/ansible_test_cli_stub.py

@ -0,0 +1,38 @@
breaking_changes:
- ansible-test - Automatic installation of requirements for "cloud" test plugins no longer occurs. The affected test plugins are
``aws``, ``azure``, ``cs``, ``hcloud``, ``nios``, ``opennebula``, ``openshift`` and ``vcenter``. Collections should instead use one of the
supported integration test requirements files, such as the ``tests/integration/requirements.txt`` file.
major_changes:
- ansible-test - Python 3.8 - 3.10 are now required to run ``ansible-test``, thus matching the Ansible controller Python requirements.
Older Python versions (2.6 - 2.7 and 3.5 - 3.10) can still be the target for relevant tests.
- ansible-test - New ``--controller`` and ``--target`` / ``--target-python`` options have been added to allow more control over test environments.
- ansible-test - Integration tests run with the ``integration`` command can now be executed on two separate hosts instead of always running on the controller.
The target host can be one provided by ``ansible-test`` or by the user, as long as it is accessible using SSH.
- ansible-test - Collections can now specify controller and target specific integration test requirements and constraints.
If provided, they take precedence over the previously available requirements and constraints files.
- ansible-test - Sanity tests always run in isolated Python virtual environments specific to the requirements of each test. The environments are cached.
- ansible-test - Sanity tests now use fully pinned requirements that are independent of each other and other test types.
- ansible-test - Sanity tests are now separated into two categories, controller and target. All tests except ``import`` and ``compile`` are controller tests.
The controller tests always run using the same Python version used to run ``ansible-test``.
The target tests use the Python version(s) specified by the user, or all available Python versions.
- junit callback - The ``junit_xml`` and ``ordereddict`` Python modules are no longer required to use the ``junit`` callback plugin.
minor_changes:
- ansible-test - Using an unknown ``--docker`` or ``--remote`` environment now requires specifying a Python version.
- ansible-test - The ``--docker-keep-git`` option (used only for testing ansible-core) has been renamed to ``--keep-git``.
- ansible-test - A new ``base`` test container is available.
It is similar to the ``default`` test container, but contains no pre-installed Python packages other than ``pip`` and its dependencies.
- ansible-test - Default settings are now applied to unknown versions of known ``--remote`` platforms.
- ansible-test - Constraints provided by ``ansible-test`` for Python package installs have been reduced.
- ansible-test - Command line help has been updated to hide the ``--remote`` option (and related options) when the user lacks an API key to use the feature.
- ansible-test - The ``--python`` option can be used without another delegation option such as the ``--venv`` or ``--docker`` options.
- ansible-test - Environment checking (``pip``, ``python``, ``~/.ssh/known_hosts``, etc.) is no longer performed when running integration tests.
- ansible-test - Most scripts used internally by ``ansible-test`` no longer have a shebang or the executable bit set.
bugfixes:
- ansible-test - Tab completion after options like ``--docker`` which accept an optional argument will no longer provide incorrect completions.
- ansible-test - The ``--python`` and ``--venv`` options are no longer ignored by some commands, such as ``coverage``.
known_issues:
- ansible-test - Tab completion anywhere other than the end of the command with the new composite options will provide incorrect results.
See https://github.com/kislyuk/argcomplete/issues/351 for additional details.
deprecated_features:
- ansible-test - The ``--docker-no-pull`` option is deprecated and has no effect.
- ansible-test - The ``--no-pip-check`` option is deprecated and has no effect.

@ -0,0 +1,8 @@
ansible-test-future-boilerplate
===============================
The ``_internal`` code for ``ansible-test`` requires the following ``__future__`` import:
.. code-block:: python
from __future__ import annotations

@ -24,7 +24,7 @@ How to run
.. note:: .. note::
When using docker and the ``--base-branch`` argument, When using docker and the ``--base-branch`` argument,
also use the ``--docker-keep-git`` argument to avoid git related errors. also use the ``--keep-git`` argument to avoid git related errors.
.. code:: shell .. code:: shell

@ -347,7 +347,7 @@ if __name__ == '__main__':
''' '''
ANSIBALLZ_COVERAGE_TEMPLATE = ''' ANSIBALLZ_COVERAGE_TEMPLATE = '''
os.environ['COVERAGE_FILE'] = '%(coverage_output)s' os.environ['COVERAGE_FILE'] = '%(coverage_output)s=python-%%s=coverage' %% '.'.join(str(v) for v in sys.version_info[:2])
import atexit import atexit

@ -73,8 +73,7 @@ DOCUMENTATION = '''
env: env:
- name: JUNIT_TEST_CASE_PREFIX - name: JUNIT_TEST_CASE_PREFIX
requirements: requirements:
- whitelist in configuration - enable in configuration
- junit_xml (python lib)
''' '''
import os import os
@ -84,31 +83,13 @@ import re
from ansible import constants as C from ansible import constants as C
from ansible.module_utils._text import to_bytes, to_text from ansible.module_utils._text import to_bytes, to_text
from ansible.plugins.callback import CallbackBase from ansible.plugins.callback import CallbackBase
from ansible.utils._junit_xml import (
try: TestCase,
from junit_xml import TestSuite, TestCase TestError,
TestFailure,
# the junit_xml API is changing in version 2.0.0 TestSuite,
# TestSuite.to_xml_string is being replaced with to_xml_report_string TestSuites,
# see: https://github.com/kyrus/python-junit-xml/blob/63db26da353790500642fd02cae1543eb41aab8b/junit_xml/__init__.py#L249-L261 )
try:
from junit_xml import to_xml_report_string
except ImportError:
to_xml_report_string = TestSuite.to_xml_string
HAS_JUNIT_XML = True
except ImportError:
HAS_JUNIT_XML = False
try:
from collections import OrderedDict
HAS_ORDERED_DICT = True
except ImportError:
try:
from ordereddict import OrderedDict
HAS_ORDERED_DICT = True
except ImportError:
HAS_ORDERED_DICT = False
class CallbackModule(CallbackBase): class CallbackModule(CallbackBase):
@ -142,10 +123,6 @@ class CallbackModule(CallbackBase):
JUNIT_TEST_CASE_PREFIX (optional): Consider a task only as test case if it has this value as prefix. Additionaly failing tasks are recorded as failed JUNIT_TEST_CASE_PREFIX (optional): Consider a task only as test case if it has this value as prefix. Additionaly failing tasks are recorded as failed
test cases. test cases.
Default: <empty> Default: <empty>
Requires:
junit_xml
""" """
CALLBACK_VERSION = 2.0 CALLBACK_VERSION = 2.0
@ -171,17 +148,7 @@ class CallbackModule(CallbackBase):
self.disabled = False self.disabled = False
if not HAS_JUNIT_XML: self._task_data = {}
self.disabled = True
self._display.warning('The `junit_xml` python module is not installed. '
'Disabling the `junit` callback plugin.')
if HAS_ORDERED_DICT:
self._task_data = OrderedDict()
else:
self.disabled = True
self._display.warning('The `ordereddict` python module is not installed. '
'Disabling the `junit` callback plugin.')
if not os.path.exists(self._output_dir): if not os.path.exists(self._output_dir):
os.makedirs(self._output_dir) os.makedirs(self._output_dir)
@ -250,7 +217,7 @@ class CallbackModule(CallbackBase):
junit_classname = re.sub(r'\.yml:[0-9]+$', '', junit_classname) junit_classname = re.sub(r'\.yml:[0-9]+$', '', junit_classname)
if host_data.status == 'included': if host_data.status == 'included':
return TestCase(name, junit_classname, duration, host_data.result) return TestCase(name=name, classname=junit_classname, time=duration, system_out=str(host_data.result))
res = host_data.result._result res = host_data.result._result
rc = res.get('rc', 0) rc = res.get('rc', 0)
@ -258,26 +225,26 @@ class CallbackModule(CallbackBase):
dump = self._cleanse_string(dump) dump = self._cleanse_string(dump)
if host_data.status == 'ok': if host_data.status == 'ok':
return TestCase(name, junit_classname, duration, dump) return TestCase(name=name, classname=junit_classname, time=duration, system_out=dump)
test_case = TestCase(name, junit_classname, duration) test_case = TestCase(name=name, classname=junit_classname, time=duration)
if host_data.status == 'failed': if host_data.status == 'failed':
if 'exception' in res: if 'exception' in res:
message = res['exception'].strip().split('\n')[-1] message = res['exception'].strip().split('\n')[-1]
output = res['exception'] output = res['exception']
test_case.add_error_info(message, output) test_case.errors.append(TestError(message=message, output=output))
elif 'msg' in res: elif 'msg' in res:
message = res['msg'] message = res['msg']
test_case.add_failure_info(message, dump) test_case.failures.append(TestFailure(message=message, output=dump))
else: else:
test_case.add_failure_info('rc=%s' % rc, dump) test_case.failures.append(TestFailure(message='rc=%s' % rc, output=dump))
elif host_data.status == 'skipped': elif host_data.status == 'skipped':
if 'skip_reason' in res: if 'skip_reason' in res:
message = res['skip_reason'] message = res['skip_reason']
else: else:
message = 'skipped' message = 'skipped'
test_case.add_skipped_info(message) test_case.skipped = message
return test_case return test_case
@ -297,8 +264,9 @@ class CallbackModule(CallbackBase):
for host_uuid, host_data in task_data.host_data.items(): for host_uuid, host_data in task_data.host_data.items():
test_cases.append(self._build_test_case(task_data, host_data)) test_cases.append(self._build_test_case(task_data, host_data))
test_suite = TestSuite(self._playbook_name, test_cases) test_suite = TestSuite(name=self._playbook_name, cases=test_cases)
report = to_xml_report_string([test_suite]) test_suites = TestSuites(suites=[test_suite])
report = test_suites.to_pretty_xml()
output_file = os.path.join(self._output_dir, '%s-%s.xml' % (self._playbook_name, time.time())) output_file = os.path.join(self._output_dir, '%s-%s.xml' % (self._playbook_name, time.time()))
@ -354,7 +322,7 @@ class TaskData:
self.path = path self.path = path
self.play = play self.play = play
self.start = None self.start = None
self.host_data = OrderedDict() self.host_data = {}
self.start = time.time() self.start = time.time()
self.action = action self.action = action

@ -0,0 +1,268 @@
"""
Dataclasses for creating JUnit XML files.
See: https://github.com/junit-team/junit5/blob/main/platform-tests/src/test/resources/jenkins-junit.xsd
"""
from __future__ import annotations
import abc
import dataclasses
import datetime
import decimal
import typing as t
from xml.dom import minidom
# noinspection PyPep8Naming
from xml.etree import ElementTree as ET
@dataclasses.dataclass
class TestResult(metaclass=abc.ABCMeta):
"""Base class for the result of a test case."""
output: t.Optional[str] = None
message: t.Optional[str] = None
type: t.Optional[str] = None
def __post_init__(self):
if self.type is None:
self.type = self.tag
@property
@abc.abstractmethod
def tag(self) -> str:
"""Tag name for the XML element created by this result type."""
def get_attributes(self) -> t.Dict[str, str]:
"""Return a dictionary of attributes for this instance."""
return _attributes(
message=self.message,
type=self.type,
)
def get_xml_element(self) -> ET.Element:
"""Return an XML element representing this instance."""
element = ET.Element(self.tag, self.get_attributes())
element.text = self.output
return element
@dataclasses.dataclass
class TestFailure(TestResult):
"""Failure info for a test case."""
@property
def tag(self) -> str:
"""Tag name for the XML element created by this result type."""
return 'failure'
@dataclasses.dataclass
class TestError(TestResult):
"""Error info for a test case."""
@property
def tag(self) -> str:
"""Tag name for the XML element created by this result type."""
return 'error'
@dataclasses.dataclass
class TestCase:
"""An individual test case."""
name: str
assertions: t.Optional[int] = None
classname: t.Optional[str] = None
status: t.Optional[str] = None
time: t.Optional[decimal.Decimal] = None
errors: t.List[TestError] = dataclasses.field(default_factory=list)
failures: t.List[TestFailure] = dataclasses.field(default_factory=list)
skipped: t.Optional[str] = None
system_out: t.Optional[str] = None
system_err: t.Optional[str] = None
is_disabled: bool = False
@property
def is_failure(self) -> bool:
"""True if the test case contains failure info."""
return bool(self.failures)
@property
def is_error(self) -> bool:
"""True if the test case contains error info."""
return bool(self.errors)
@property
def is_skipped(self) -> bool:
"""True if the test case was skipped."""
return bool(self.skipped)
def get_attributes(self) -> t.Dict[str, str]:
"""Return a dictionary of attributes for this instance."""
return _attributes(
assertions=self.assertions,
classname=self.classname,
name=self.name,
status=self.status,
time=self.time,
)
def get_xml_element(self) -> ET.Element:
"""Return an XML element representing this instance."""
element = ET.Element('testcase', self.get_attributes())
if self.skipped:
ET.SubElement(element, 'skipped').text = self.skipped
element.extend([error.get_xml_element() for error in self.errors])
element.extend([failure.get_xml_element() for failure in self.failures])
if self.system_out:
ET.SubElement(element, 'system-out').text = self.system_out
if self.system_err:
ET.SubElement(element, 'system-err').text = self.system_err
return element
@dataclasses.dataclass
class TestSuite:
"""A collection of test cases."""
name: str
hostname: t.Optional[str] = None
id: t.Optional[str] = None
package: t.Optional[str] = None
timestamp: t.Optional[datetime.datetime] = None
properties: t.Dict[str, str] = dataclasses.field(default_factory=dict)
cases: t.List[TestCase] = dataclasses.field(default_factory=list)
system_out: t.Optional[str] = None
system_err: t.Optional[str] = None
@property
def disabled(self) -> int:
"""The number of disabled test cases."""
return sum(case.is_disabled for case in self.cases)
@property
def errors(self) -> int:
"""The number of test cases containing error info."""
return sum(case.is_error for case in self.cases)
@property
def failures(self) -> int:
"""The number of test cases containing failure info."""
return sum(case.is_failure for case in self.cases)
@property
def skipped(self) -> int:
"""The number of test cases containing skipped info."""
return sum(case.is_skipped for case in self.cases)
@property
def tests(self) -> int:
"""The number of test cases."""
return len(self.cases)
@property
def time(self) -> decimal.Decimal:
"""The total time from all test cases."""
return sum(case.time for case in self.cases if case.time)
def get_attributes(self) -> t.Dict[str, str]:
"""Return a dictionary of attributes for this instance."""
return _attributes(
disabled=self.disabled,
errors=self.errors,
failures=self.failures,
hostname=self.hostname,
id=self.id,
name=self.name,
package=self.package,
skipped=self.skipped,
tests=self.tests,
time=self.time,
timestamp=self.timestamp.isoformat(timespec='seconds') if self.timestamp else None,
)
def get_xml_element(self) -> ET.Element:
"""Return an XML element representing this instance."""
element = ET.Element('testsuite', self.get_attributes())
if self.properties:
ET.SubElement(element, 'properties').extend([ET.Element('property', dict(name=name, value=value)) for name, value in self.properties.items()])
element.extend([test_case.get_xml_element() for test_case in self.cases])
if self.system_out:
ET.SubElement(element, 'system-out').text = self.system_out
if self.system_err:
ET.SubElement(element, 'system-err').text = self.system_err
return element
@dataclasses.dataclass
class TestSuites:
"""A collection of test suites."""
name: t.Optional[str] = None
suites: t.List[TestSuite] = dataclasses.field(default_factory=list)
@property
def disabled(self) -> int:
"""The number of disabled test cases."""
return sum(suite.disabled for suite in self.suites)
@property
def errors(self) -> int:
"""The number of test cases containing error info."""
return sum(suite.errors for suite in self.suites)
@property
def failures(self) -> int:
"""The number of test cases containing failure info."""
return sum(suite.failures for suite in self.suites)
@property
def tests(self) -> int:
"""The number of test cases."""
return sum(suite.tests for suite in self.suites)
@property
def time(self) -> decimal.Decimal:
"""The total time from all test cases."""
return sum(suite.time for suite in self.suites)
def get_attributes(self) -> t.Dict[str, str]:
"""Return a dictionary of attributes for this instance."""
return _attributes(
disabled=self.disabled,
errors=self.errors,
failures=self.failures,
name=self.name,
tests=self.tests,
time=self.time,
)
def get_xml_element(self) -> ET.Element:
"""Return an XML element representing this instance."""
element = ET.Element('testsuites', self.get_attributes())
element.extend([suite.get_xml_element() for suite in self.suites])
return element
def to_pretty_xml(self) -> str:
"""Return a pretty formatted XML string representing this instance."""
return _pretty_xml(self.get_xml_element())
def _attributes(**kwargs) -> t.Dict[str, str]:
"""Return the given kwargs as a dictionary with values converted to strings. Items with a value of None will be omitted."""
return {key: str(value) for key, value in kwargs.items() if value is not None}
def _pretty_xml(element: ET.Element) -> str:
"""Return a pretty formatted XML string representing the given element."""
return minidom.parseString(ET.tostring(element, encoding='unicode')).toprettyxml()

@ -1 +1,2 @@
shippable/posix/group2 shippable/posix/group2
context/controller

@ -1,2 +1,3 @@
shippable/posix/group1 shippable/posix/group1
skip/aix skip/aix
context/target

@ -1 +1,2 @@
shippable/posix/group1 shippable/posix/group1
context/controller

@ -1,3 +1,2 @@
shippable/posix/group4 shippable/posix/group4
skip/aix context/controller
skip/python2.6 # ansible-galaxy uses tarfile with features not available until 2.7

@ -1,3 +1,4 @@
shippable/galaxy/group1 shippable/galaxy/group1
shippable/galaxy/smoketest shippable/galaxy/smoketest
cloud/galaxy cloud/galaxy
context/controller

@ -1,2 +1,2 @@
shippable/posix/group4 shippable/posix/group4
skip/python2.6 # build uses tarfile with features not available until 2.7 context/controller

@ -1,4 +1,3 @@
destructive destructive
shippable/posix/group4 shippable/posix/group4
skip/python2.6 # build uses tarfile with features not available until 2.7 context/controller
skip/aix

@ -1 +1,2 @@
shippable/posix/group5 shippable/posix/group5
context/controller

@ -1,2 +1,2 @@
shippable/posix/group3 shippable/posix/group3
skip/aix context/controller

@ -1,6 +1,5 @@
shippable/posix/group3 shippable/posix/group3
skip/python2 # ansible-runner is for controller and deprecated python2 support context/controller
skip/aix
skip/osx skip/osx
skip/macos skip/macos
skip/freebsd skip/freebsd

@ -1,2 +1,3 @@
cloud/acme cloud/acme
shippable/generic/group1 shippable/generic/group1
context/controller

@ -1,2 +1,3 @@
cloud/cs cloud/cs
shippable/generic/group1 shippable/generic/group1
context/controller

@ -1,2 +1,3 @@
cloud/foreman cloud/foreman
shippable/generic/group1 shippable/generic/group1
context/controller

@ -1,3 +1,4 @@
shippable/galaxy/group1 shippable/galaxy/group1
shippable/galaxy/smoketest shippable/galaxy/smoketest
cloud/galaxy cloud/galaxy
context/controller

@ -1,3 +1,4 @@
cloud/httptester cloud/httptester
windows windows
shippable/windows/group1 shippable/windows/group1
context/target

@ -1,2 +1,3 @@
needs/httptester # using legacy alias for testing purposes needs/httptester # using legacy alias for testing purposes
shippable/posix/group1 shippable/posix/group1
context/controller

@ -1,2 +1,3 @@
cloud/nios cloud/nios
shippable/generic/group1 shippable/generic/group1
context/controller

@ -1,3 +1,4 @@
cloud/openshift cloud/openshift
shippable/generic/group1 shippable/generic/group1
disabled # disabled due to requirements conflict: botocore 1.20.6 has requirement urllib3<1.27,>=1.25.4, but you have urllib3 1.24.3. disabled # disabled due to requirements conflict: botocore 1.20.6 has requirement urllib3<1.27,>=1.25.4, but you have urllib3 1.24.3.
context/controller

@ -1,2 +1,3 @@
cloud/vcenter cloud/vcenter
shippable/generic/group1 shippable/generic/group1
context/controller

@ -1 +1,2 @@
shippable/generic/group1 # Runs in the default test container so access to tools like pwsh shippable/generic/group1 # Runs in the default test container so access to tools like pwsh
context/controller

@ -1,2 +1,2 @@
shippable/posix/group1 shippable/posix/group1
skip/aix context/controller

@ -1,2 +1,2 @@
shippable/posix/group3 shippable/posix/group3
skip/aix context/controller

@ -27,7 +27,7 @@
- vaulted_value|forceescape == 'foo bar' - vaulted_value|forceescape == 'foo bar'
- vaulted_value|first == 'f' - vaulted_value|first == 'f'
- "'%s'|format(vaulted_value) == 'foo bar'" - "'%s'|format(vaulted_value) == 'foo bar'"
- vaulted_value|indent(indentfirst=True) == ' foo bar' - vaulted_value|indent(first=True) == ' foo bar'
- vaulted_value.split() == ['foo', 'bar'] - vaulted_value.split() == ['foo', 'bar']
- vaulted_value|join('-') == 'f-o-o- -b-a-r' - vaulted_value|join('-') == 'f-o-o- -b-a-r'
- vaulted_value|last == 'r' - vaulted_value|last == 'r'

@ -1,2 +1,2 @@
shippable/posix/group4 shippable/posix/group4
skip/aix context/controller

@ -1 +1,2 @@
shippable/posix/group3 shippable/posix/group3
context/controller

@ -1 +1,2 @@
shippable/posix/group3 shippable/posix/group3
context/controller

@ -1 +1,2 @@
shippable/posix/group5 shippable/posix/group5
context/controller

@ -1,2 +1,2 @@
shippable/posix/group3 shippable/posix/group3
skip/aix context/controller # this is a controller-only action, the module is just for documentation

@ -1 +1,2 @@
shippable/posix/group5 shippable/posix/group5
context/target

@ -1,3 +1,4 @@
destructive destructive
shippable/posix/group1 shippable/posix/group1
skip/aix skip/aix
context/target

@ -1,3 +1,3 @@
destructive destructive
shippable/posix/group1 shippable/posix/group1
skip/aix context/controller

@ -1,5 +1,5 @@
destructive destructive
shippable/posix/group1 shippable/posix/group1
skip/aix
needs/ssh needs/ssh
needs/root needs/root
context/controller

@ -1 +1,2 @@
shippable/posix/group2 shippable/posix/group2
context/target

@ -1,2 +1,3 @@
shippable/posix/group3 shippable/posix/group3
needs/target/binary_modules needs/target/binary_modules
context/target

@ -1 +1,2 @@
shippable/posix/group3 shippable/posix/group3
context/controller

@ -1,3 +1,4 @@
setup/always/setup_passlib setup/always/setup_passlib
setup/always/setup_pexpect setup/always/setup_pexpect
shippable/posix/group4 shippable/posix/group4
context/controller

@ -1,2 +1 @@
shippable/posix/group1 shippable/posix/group1
skip/aix

@ -1 +1,2 @@
shippable/posix/group2 shippable/posix/group2
context/controller

@ -1 +1,2 @@
shippable/posix/group3 shippable/posix/group3
context/controller

@ -3,3 +3,4 @@ needs/root
needs/ssh needs/ssh
needs/target/setup_pexpect needs/target/setup_pexpect
shippable/posix/group3 shippable/posix/group3
context/controller

@ -8,10 +8,6 @@ export ANSIBLE_GATHER_SUBSET=minimal
export ANSIBLE_HOST_PATTERN_MISMATCH=error export ANSIBLE_HOST_PATTERN_MISMATCH=error
unset ANSIBLE_COLLECTIONS_ON_ANSIBLE_VERSION_MISMATCH unset ANSIBLE_COLLECTIONS_ON_ANSIBLE_VERSION_MISMATCH
# FUTURE: just use INVENTORY_PATH as-is once ansible-test sets the right dir
ipath=../../$(basename "${INVENTORY_PATH:-../../inventory}")
export INVENTORY_PATH="$ipath"
# ensure we can call collection module # ensure we can call collection module
ansible localhost -m testns.testcoll.testmodule ansible localhost -m testns.testcoll.testmodule

@ -1 +1,2 @@
shippable/posix/group1 shippable/posix/group1
context/controller

@ -1,3 +1,2 @@
shippable/posix/group4 shippable/posix/group4
skip/python2.6 context/controller
skip/aix

@ -25,19 +25,19 @@ ansible \
=== Test that the module \ === Test that the module \
gets picked up if installed \ gets picked up if installed \
into site-packages === into site-packages ===
python -m pip.__main__ install pep517 python -m pip install pep517
( # Build a binary Python dist (a wheel) using PEP517: ( # Build a binary Python dist (a wheel) using PEP517:
cp -r ansible-collection-python-dist-boo "${OUTPUT_DIR}/" cp -r ansible-collection-python-dist-boo "${OUTPUT_DIR}/"
cd "${OUTPUT_DIR}/ansible-collection-python-dist-boo" cd "${OUTPUT_DIR}/ansible-collection-python-dist-boo"
python -m pep517.build --binary --out-dir dist . python -m pep517.build --binary --out-dir dist .
) )
# Install a pre-built dist with pip: # Install a pre-built dist with pip:
python -m pip.__main__ install \ python -m pip install \
--no-index \ --no-index \
-f "${OUTPUT_DIR}/ansible-collection-python-dist-boo/dist/" \ -f "${OUTPUT_DIR}/ansible-collection-python-dist-boo/dist/" \
--only-binary=ansible-collections.python.dist \ --only-binary=ansible-collections.python.dist \
ansible-collections.python.dist ansible-collections.python.dist
python -m pip.__main__ show ansible-collections.python.dist python -m pip show ansible-collections.python.dist
ansible \ ansible \
-m python.dist.boo \ -m python.dist.boo \
-a 'name=Frodo' \ -a 'name=Frodo' \

@ -1 +1,2 @@
shippable/posix/group2 shippable/posix/group2
context/controller

@ -504,11 +504,11 @@
when: ansible_facts.python_version is version('3', '>=') when: ansible_facts.python_version is version('3', '>=')
- name: run command with strip - name: run command with strip
command: '{{ ansible_playbook_python}} -c "import sys; msg=''hello \n \r''; print(msg); {{ print_error_command }}"' command: '{{ ansible_python_interpreter }} -c "import sys; msg=''hello \n \r''; print(msg); {{ print_error_command }}"'
register: command_strip register: command_strip
- name: run command without strip - name: run command without strip
command: '{{ ansible_playbook_python}} -c "import sys; msg=''hello \n \r''; print(msg); {{ print_error_command }}"' command: '{{ ansible_python_interpreter }} -c "import sys; msg=''hello \n \r''; print(msg); {{ print_error_command }}"'
args: args:
strip_empty_ends: no strip_empty_ends: no
register: command_no_strip register: command_no_strip

@ -1 +1,2 @@
shippable/posix/group5 shippable/posix/group5
context/controller

@ -1 +1,2 @@
shippable/posix/group1 shippable/posix/group1
context/controller

@ -1 +1,2 @@
shippable/posix/group1 shippable/posix/group1
context/controller

@ -1,4 +1,5 @@
shippable/posix/group1 shippable/posix/group1
context/controller
skip/freebsd # No sshpass skip/freebsd # No sshpass
skip/osx # No sshpass skip/osx # No sshpass
skip/macos # No sshpass skip/macos # No sshpass

@ -2,4 +2,3 @@ needs/ssh
shippable/posix/group3 shippable/posix/group3
needs/target/setup_paramiko needs/target/setup_paramiko
destructive # potentially installs/uninstalls OS packages via setup_paramiko destructive # potentially installs/uninstalls OS packages via setup_paramiko
skip/aix

@ -1,3 +1,2 @@
needs/ssh needs/ssh
shippable/posix/group1 shippable/posix/group1
skip/aix

@ -0,0 +1,2 @@
context/controller
shippable/posix/group1

@ -0,0 +1,9 @@
- name: Verify testhost is control host
stat:
path: "{{ output_dir }}"
- name: Get control host details
setup:
register: control_host
- name: Show control host details
debug:
msg: "{{ control_host.ansible_facts.ansible_distribution }} {{ control_host.ansible_facts.ansible_distribution_version }}"

@ -1 +1,2 @@
shippable/posix/group1 shippable/posix/group1
context/controller

@ -1 +1,2 @@
shippable/posix/group1 shippable/posix/group1
context/controller # this is a controller-only action, the module is just for documentation

@ -1,4 +1,4 @@
shippable/posix/group3 shippable/posix/group3
needs/ssh needs/ssh
needs/root # only on macOS and FreeBSD to configure network interfaces needs/root # only on macOS and FreeBSD to configure network interfaces
skip/aix context/controller

@ -1 +1,2 @@
shippable/posix/group1 shippable/posix/group1
context/controller

@ -700,7 +700,7 @@
content: | content: |
[main] [main]
exclude=lsof* exclude=lsof*
dest: '{{ output_dir }}/test-dnf.conf' dest: '{{ remote_tmp_dir }}/test-dnf.conf'
register: test_dnf_copy register: test_dnf_copy
- block: - block:
@ -728,7 +728,7 @@
always: always:
- name: remove exclude lsof conf file - name: remove exclude lsof conf file
file: file:
path: '{{ output_dir }}/test-dnf.conf' path: '{{ remote_tmp_dir }}/test-dnf.conf'
state: absent state: absent
# end test case where disable_excludes is supported # end test case where disable_excludes is supported

@ -1 +1,2 @@
shippable/posix/group1 shippable/posix/group1
context/controller

@ -1 +1,2 @@
shippable/posix/group2 shippable/posix/group2
context/target

@ -1 +1,2 @@
shippable/posix/group3 shippable/posix/group3
context/target

@ -1 +1,2 @@
shippable/posix/group2 shippable/posix/group2
context/controller

@ -1 +1,2 @@
shippable/posix/group2 shippable/posix/group2
context/controller

@ -3,3 +3,4 @@ shippable/posix/group2
skip/freebsd skip/freebsd
skip/osx skip/osx
skip/macos skip/macos
context/controller

@ -1 +1,2 @@
shippable/posix/group2 shippable/posix/group2
context/controller

@ -1,2 +1,3 @@
shippable/posix/group2 shippable/posix/group2
needs/target/setup_remote_tmp_dir needs/target/setup_remote_tmp_dir
needs/ssh

@ -1,8 +0,0 @@
all:
hosts:
testhost:
ansible_host: localhost
ansible_connection: ssh
ansible_python_interpreter: "{{ ansible_playbook_python }}"
ansible_host_key_checking: no
ansible_ssh_common_args: -o UserKnownHostsFile={{ output_dir }}/known_hosts -o StrictHostKeyChecking=no

@ -3,7 +3,7 @@
set -eux set -eux
function cleanup { function cleanup {
ansible-playbook -i hosts.yml cleanup.yml -e "output_dir=${OUTPUT_DIR}" -b "$@" ansible-playbook -i "${INVENTORY_PATH}" cleanup.yml -e "output_dir=${OUTPUT_DIR}" -b "$@"
unset ANSIBLE_CACHE_PLUGIN unset ANSIBLE_CACHE_PLUGIN
unset ANSIBLE_CACHE_PLUGIN_CONNECTION unset ANSIBLE_CACHE_PLUGIN_CONNECTION
} }
@ -28,7 +28,7 @@ ansible-playbook -i ../../inventory injection/avoid_slurp_return.yml -e "output_
export ANSIBLE_CACHE_PLUGIN=jsonfile export ANSIBLE_CACHE_PLUGIN=jsonfile
export ANSIBLE_CACHE_PLUGIN_CONNECTION="${OUTPUT_DIR}/cache" export ANSIBLE_CACHE_PLUGIN_CONNECTION="${OUTPUT_DIR}/cache"
# Create a non-root user account and configure SSH acccess for that account # Create a non-root user account and configure SSH acccess for that account
ansible-playbook -i hosts.yml setup_unreadable_test.yml -e "output_dir=${OUTPUT_DIR}" "$@" ansible-playbook -i "${INVENTORY_PATH}" setup_unreadable_test.yml -e "output_dir=${OUTPUT_DIR}" "$@"
# Run the tests as the unprivileged user without become to test the use of the stat module from the fetch module # Run the tests as the unprivileged user without become to test the use of the stat module from the fetch module
ansible-playbook --user fetcher -i hosts.yml test_unreadable_with_stat.yml -e "output_dir=${OUTPUT_DIR}" "$@" ansible-playbook -i "${INVENTORY_PATH}" test_unreadable_with_stat.yml -e ansible_user=fetcher -e ansible_become=no -e "output_dir=${OUTPUT_DIR}" "$@"

@ -91,7 +91,10 @@
- "file2_result.state == 'absent'" - "file2_result.state == 'absent'"
- name: verify we can touch a file - name: verify we can touch a file
file: path={{output_dir}}/baz.txt state=touch file:
path: "{{output_dir}}/baz.txt"
state: touch
mode: '0644'
register: file3_result register: file3_result
- name: verify that the file was marked as changed - name: verify that the file was marked as changed

@ -1,3 +1 @@
shippable/posix/group2 shippable/posix/group2
skip/python2.6 # filters are controller only, and we no longer support Python 2.6 on the controller
skip/aix

@ -1,4 +1 @@
shippable/posix/group2 shippable/posix/group2
skip/python2.6 # filters are controller only, and we no longer support Python 2.6 on the controller
skip/python2.7 # filters are controller only, and we no longer support Python 2.7 on the controller
skip/aix

@ -1,3 +1 @@
shippable/posix/group2 shippable/posix/group2
skip/python2.6 # filters are controller only, and we no longer support Python 2.6 on the controller
skip/aix

@ -1,3 +1 @@
shippable/posix/group2 shippable/posix/group2
skip/python2.6 # filters are controller only, and we no longer support Python 2.6 on the controller
skip/aix

@ -1,3 +1 @@
shippable/posix/group2 shippable/posix/group2
skip/python2.6 # filters are controller only, and we no longer support Python 2.6 on the controller
skip/aix

@ -1 +1,2 @@
shippable/posix/group3 shippable/posix/group3
context/controller

@ -1,2 +1,3 @@
shippable/posix/group3 shippable/posix/group3
needs/root needs/root
context/controller

@ -1,2 +1,3 @@
shippable/posix/group2 shippable/posix/group2
needs/file/test/lib/ansible_test/_data/requirements/constraints.txt needs/file/test/lib/ansible_test/_data/requirements/constraints.txt
context/controller

@ -1,3 +1,2 @@
shippable/posix/group5 shippable/posix/group5
handler_race context/controller
skip/aix

@ -1,3 +1,2 @@
shippable/posix/group5 shippable/posix/group5
handlers context/controller
skip/aix

@ -1,3 +1,4 @@
destructive destructive
needs/privileged needs/privileged
shippable/posix/group2 shippable/posix/group2
context/controller

@ -1 +1,2 @@
shippable/posix/group3 shippable/posix/group3
context/controller

@ -1 +1,2 @@
shippable/posix/group3 shippable/posix/group3
context/controller

@ -1 +1,2 @@
shippable/posix/group4 shippable/posix/group4
context/controller

@ -1 +1,2 @@
shippable/posix/group3 shippable/posix/group3
context/controller

@ -1,2 +1,2 @@
shippable/posix/group5 shippable/posix/group5
skip/aix context/controller # this is a controller-only action, the module is just for documentation

@ -4,3 +4,4 @@ skip/aix
skip/osx skip/osx
skip/macos skip/macos
skip/freebsd skip/freebsd
context/target

@ -1 +1,2 @@
shippable/posix/incidental shippable/posix/incidental
context/target

@ -1,2 +1,3 @@
cloud/aws cloud/aws
shippable/aws/incidental shippable/aws/incidental
context/controller

@ -2,6 +2,10 @@
set -eux set -eux
source virtualenv.sh
python -m pip install boto3 boto
# ensure test config is empty # ensure test config is empty
ansible-playbook playbooks/empty_inventory_config.yml "$@" ansible-playbook playbooks/empty_inventory_config.yml "$@"

@ -1,6 +1,5 @@
shippable/posix/incidental shippable/posix/incidental
skip/aix context/controller
skip/power/centos
skip/osx skip/osx
skip/macos skip/macos
skip/freebsd skip/freebsd

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save