mirror of https://github.com/ansible/ansible.git
Update ansible-test layout and payload handling.
parent
07051473f8
commit
39b3fc0926
@ -0,0 +1,7 @@
|
||||
bin-symlinks
|
||||
============
|
||||
|
||||
The ``bin/`` directory in Ansible must contain only symbolic links to executable files.
|
||||
These files must reside in the ``lib/ansible/`` or ``test/lib/ansible_test/`` directories.
|
||||
|
||||
This is required to allow ``ansible-test`` to work with containers and remote hosts when running from an installed version of Ansible.
|
@ -0,0 +1,41 @@
|
||||
"""Source provider for content which has been installed."""
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import os
|
||||
|
||||
from ... import types as t
|
||||
|
||||
from . import (
|
||||
SourceProvider,
|
||||
)
|
||||
|
||||
|
||||
class InstalledSource(SourceProvider):
|
||||
"""Source provider for content which has been installed."""
|
||||
sequence = 0 # disable automatic detection
|
||||
|
||||
@staticmethod
|
||||
def is_content_root(path): # type: (str) -> bool
|
||||
"""Return True if the given path is a content root for this provider."""
|
||||
return False
|
||||
|
||||
def get_paths(self, path): # type: (str) -> t.List[str]
|
||||
"""Return the list of available content paths under the given path."""
|
||||
paths = []
|
||||
|
||||
kill_extensions = (
|
||||
'.pyc',
|
||||
'.pyo',
|
||||
)
|
||||
|
||||
for root, _dummy, file_names in os.walk(path):
|
||||
rel_root = os.path.relpath(root, path)
|
||||
|
||||
if rel_root == '.':
|
||||
rel_root = ''
|
||||
|
||||
paths.extend([os.path.join(rel_root, file_name) for file_name in file_names
|
||||
if not os.path.splitext(file_name)[1] in kill_extensions])
|
||||
|
||||
return paths
|
@ -0,0 +1,97 @@
|
||||
"""Sanity test for symlinks in the bin directory."""
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
||||
import os
|
||||
|
||||
from .. import types as t
|
||||
|
||||
from ..sanity import (
|
||||
SanityVersionNeutral,
|
||||
SanityMessage,
|
||||
SanityFailure,
|
||||
SanitySuccess,
|
||||
)
|
||||
|
||||
from ..config import (
|
||||
SanityConfig,
|
||||
)
|
||||
|
||||
from ..data import (
|
||||
data_context,
|
||||
)
|
||||
|
||||
from ..payload import (
|
||||
ANSIBLE_BIN_SYMLINK_MAP,
|
||||
__file__ as symlink_map_full_path,
|
||||
)
|
||||
|
||||
|
||||
class BinSymlinksTest(SanityVersionNeutral):
|
||||
"""Sanity test for symlinks in the bin directory."""
|
||||
ansible_only = True
|
||||
|
||||
@property
|
||||
def can_ignore(self): # type: () -> bool
|
||||
"""True if the test supports ignore entries."""
|
||||
return False
|
||||
|
||||
@property
|
||||
def no_targets(self): # type: () -> bool
|
||||
"""True if the test does not use test targets. Mutually exclusive with all_targets."""
|
||||
return True
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
def test(self, args, targets): # pylint: disable=locally-disabled, unused-argument
|
||||
"""
|
||||
:type args: SanityConfig
|
||||
:type targets: SanityTargets
|
||||
:rtype: TestResult
|
||||
"""
|
||||
bin_root = os.path.join(data_context().content.root, 'bin')
|
||||
bin_names = os.listdir(bin_root)
|
||||
bin_paths = sorted(os.path.join(bin_root, path) for path in bin_names)
|
||||
|
||||
errors = [] # type: t.List[t.Tuple[str, str]]
|
||||
|
||||
symlink_map_path = os.path.relpath(symlink_map_full_path, data_context().content.root)
|
||||
|
||||
for bin_path in bin_paths:
|
||||
if not os.path.islink(bin_path):
|
||||
errors.append((bin_path, 'not a symbolic link'))
|
||||
continue
|
||||
|
||||
dest = os.readlink(bin_path)
|
||||
|
||||
if not os.path.exists(bin_path):
|
||||
errors.append((bin_path, 'points to non-existent path "%s"' % dest))
|
||||
continue
|
||||
|
||||
if not os.path.isfile(bin_path):
|
||||
errors.append((bin_path, 'points to non-file "%s"' % dest))
|
||||
continue
|
||||
|
||||
map_dest = ANSIBLE_BIN_SYMLINK_MAP.get(os.path.basename(bin_path))
|
||||
|
||||
if not map_dest:
|
||||
errors.append((bin_path, 'missing from ANSIBLE_BIN_SYMLINK_MAP in file "%s"' % symlink_map_path))
|
||||
continue
|
||||
|
||||
if dest != map_dest:
|
||||
errors.append((bin_path, 'points to "%s" instead of "%s" from ANSIBLE_BIN_SYMLINK_MAP in file "%s"' % (dest, map_dest, symlink_map_path)))
|
||||
continue
|
||||
|
||||
if not os.access(bin_path, os.X_OK):
|
||||
errors.append((bin_path, 'points to non-executable file "%s"' % dest))
|
||||
continue
|
||||
|
||||
for bin_path, dest in ANSIBLE_BIN_SYMLINK_MAP.items():
|
||||
if bin_path not in bin_names:
|
||||
errors.append((bin_path, 'missing symlink to "%s" defined in ANSIBLE_BIN_SYMLINK_MAP in file "%s"' % (dest, symlink_map_path)))
|
||||
|
||||
messages = [SanityMessage(message=message, path=os.path.relpath(path, data_context().content.root), confidence=100) for path, message in errors]
|
||||
|
||||
if errors:
|
||||
return SanityFailure(self.name, messages=messages)
|
||||
|
||||
return SanitySuccess(self.name)
|
Loading…
Reference in New Issue