You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ansible/test/lib/ansible_test/_internal/sanity/bin_symlinks.py

111 lines
3.5 KiB
Python

"""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,
)
from ..util import (
ANSIBLE_BIN_PATH,
ANSIBLE_TEST_DATA_ROOT,
)
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 = ANSIBLE_BIN_PATH
bin_names = os.listdir(bin_root)
bin_paths = sorted(os.path.join(bin_root, path) for path in bin_names)
injector_root = os.path.join(ANSIBLE_TEST_DATA_ROOT, 'injector')
injector_names = os.listdir(injector_root)
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_name, dest in ANSIBLE_BIN_SYMLINK_MAP.items():
if bin_name not in bin_names:
bin_path = os.path.join(bin_root, bin_name)
errors.append((bin_path, 'missing symlink to "%s" defined in ANSIBLE_BIN_SYMLINK_MAP in file "%s"' % (dest, symlink_map_path)))
if bin_name not in injector_names:
injector_path = os.path.join(injector_root, bin_name)
errors.append((injector_path, 'missing symlink to "python.py"'))
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)