97 lines
3.1 KiB
Python
97 lines
3.1 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,
|
|
)
|
|
|
|
|
|
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)
|