Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite ansible version checking #1383

Merged
merged 1 commit into from Feb 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 11 additions & 4 deletions src/ansiblelint/__main__.py
Expand Up @@ -42,6 +42,7 @@
render_yaml,
)
from ansiblelint.config import options, used_old_tags
from ansiblelint.constants import ANSIBLE_MISSING_RC
from ansiblelint.file_utils import cwd
from ansiblelint.skip_utils import normalize_tag
from ansiblelint.version import __version__
Expand Down Expand Up @@ -75,9 +76,15 @@ def initialize_options(arguments: List[str]):
new_options.cwd = pathlib.Path.cwd()

if new_options.version:
print('ansible-lint {ver!s}'.format(ver=__version__))
# assure we fail if ansible is missing, even for version printing
check_ansible_presence()
ansible_version, err = check_ansible_presence()
print(
'ansible-lint {ver!s} using ansible {ansible_ver!s}'.format(
ver=__version__, ansible_ver=ansible_version
)
)
if err:
print(err, file=sys.stderr)
sys.exit(ANSIBLE_MISSING_RC)
sys.exit(0)

if new_options.colored is None:
Expand Down Expand Up @@ -163,7 +170,7 @@ def main(argv: List[str] = None) -> int:
app = App(options=options)

prepare_environment()
check_ansible_presence()
check_ansible_presence(exit_on_error=True)

# On purpose lazy-imports to avoid pre-loading Ansible
# pylint: disable=import-outside-toplevel
Expand Down
85 changes: 63 additions & 22 deletions src/ansiblelint/_prerun.py
Expand Up @@ -3,7 +3,8 @@
import re
import subprocess
import sys
from typing import List, Optional
from functools import lru_cache
from typing import List, Optional, Tuple

from packaging import version

Expand All @@ -29,30 +30,70 @@
"""


def check_ansible_presence() -> None:
"""Assures we stop execution if Ansible is missing."""
failed = False
try:
# pylint: disable=import-outside-toplevel
from ansible import release
def check_ansible_presence(exit_on_error=False) -> Tuple[str, str]:
"""Assures we stop execution if Ansible is missing or outdated.

if version.parse(release.__version__) <= version.parse(ANSIBLE_MIN_VERSION):
failed = True
except (ImportError, ModuleNotFoundError, UnboundLocalError) as e:
failed = True
__version__ = "none"
print(e, file=sys.stderr)
if failed:
print(
"FATAL: ansible-lint requires a version of Ansible package"
" >= %s, but %s was found. "
"Please install a compatible version using the same python interpreter. See "
"https://docs.ansible.com/ansible/latest/installation_guide"
"/intro_installation.html#installing-ansible-with-pip"
% (ANSIBLE_MIN_VERSION, __version__),
file=sys.stderr,
Returne found version and an optional exception if something wrong
was detected.
"""

@lru_cache()
def _get_ver_err() -> Tuple[str, str]:

err = ""
failed = False
ver = ""
result = subprocess.run(
args=["ansible", "--version"],
stdout=subprocess.PIPE,
universal_newlines=True,
check=False,
)
if result.returncode != 0:
return (
ver,
"FATAL: Unable to retrieve ansible cli version: %s" % result.stdout,
)

match = re.match(r"^ansible ([^\s]+)", result.stdout)
if not match:
return ver, "FATAL: Unable parse ansible cli version: %s" % result.stdout
ver = match.group(1)
try:
# pylint: disable=import-outside-toplevel
from ansible.release import __version__ as ansible_module_version

if version.parse(ansible_module_version) < version.parse(
ANSIBLE_MIN_VERSION
):
failed = True
except (ImportError, ModuleNotFoundError) as e:
failed = True
ansible_module_version = "none"
err += f"{e}\n"
if failed:
err += (
"FATAL: ansible-lint requires a version of Ansible package"
" >= %s, but %s was found. "
"Please install a compatible version using the same python interpreter. See "
"https://docs.ansible.com/ansible/latest/installation_guide"
"/intro_installation.html#installing-ansible-with-pip"
% (ANSIBLE_MIN_VERSION, ansible_module_version)
)

elif ver != ansible_module_version:
err = (
f"FATAL: Ansible CLI ({ver}) and python module"
f" ({ansible_module_version}) versions do not match. This "
"indicates a broken execution environment."
)
return ver, err

ver, err = _get_ver_err()
if exit_on_error and err:
print(err, file=sys.stderr)
sys.exit(ANSIBLE_MISSING_RC)
return ver, err


def prepare_environment() -> None:
Expand Down