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