diff --git a/ansible_navigator/_version.py b/ansible_navigator/_version.py index 94893fbfc..610bc0b20 100644 --- a/ansible_navigator/_version.py +++ b/ansible_navigator/_version.py @@ -15,5 +15,5 @@ application version, although keeping the major in sync is probably not a bad idea to minimize the amount of stale docs in the user's cache """ -__version__ = "1.0.0rc2" +__version__ = "1.0.0" __version_collection_doc_cache__ = "1.0" diff --git a/ansible_navigator/actions/collections.py b/ansible_navigator/actions/collections.py index b93c53799..8749be697 100644 --- a/ansible_navigator/actions/collections.py +++ b/ansible_navigator/actions/collections.py @@ -304,7 +304,7 @@ def _run_runner(self) -> None: self._logger.debug("running collections command with execution environment enabled") python_exec_path = "python3" - container_volume_mounts = [f"{share_directory}/utils:{share_directory}/utils:z"] + container_volume_mounts = [f"{share_directory}/utils:{share_directory}/utils"] if os.path.exists(self._adjacent_collection_dir): container_volume_mounts.append( f"{self._adjacent_collection_dir}:{self._adjacent_collection_dir}:z" diff --git a/ansible_navigator/actions/images.py b/ansible_navigator/actions/images.py index 85e763afd..9f9802298 100644 --- a/ansible_navigator/actions/images.py +++ b/ansible_navigator/actions/images.py @@ -207,7 +207,9 @@ def _build_image_content(self) -> Step: message = "Collecting image details, this may take a minute..." notification = nonblocking_notification(messages=[message]) self._interaction.ui.show(notification) - self._introspect_image() + introspection_success = self._introspect_image() + if introspection_success is False: + return self.steps.previous if self.steps.current.index == 1: step = Step( @@ -345,11 +347,15 @@ def _collect_image_list(self): image["execution_environment"] = False self._images.value = sorted(images, key=lambda i: i["name"]) - def _introspect_image(self): + def _introspect_image(self) -> bool: + + if self._images.selected is None: + # an image should always be selected by now + return False self._images.selected["__introspected"] = True share_directory = self._args.internals.share_directory - container_volume_mounts = [f"{share_directory}/utils:{share_directory}/utils:z"] + container_volume_mounts = [f"{share_directory}/utils:{share_directory}/utils"] python_exec_path = "python3" kwargs = { @@ -365,8 +371,21 @@ def _introspect_image(self): ) _runner = CommandRunner(executable_cmd=python_exec_path, **kwargs) output, error, _ = _runner.run() - if not error: - parsed = self._parse(output) + if error: + self._logger.error( + "Image introspection failed (runner), the return value was: %s", error + ) + self.notify_failed() + return False + parsed = self._parse(output) + if parsed is None: + self._logger.error( + "Image introspection failed (parsed), the return value was: %s", output[0:1000] + ) + self.notify_failed() + return False + + try: self._images.selected["general"] = { "os": parsed["os_release"], "friendly": parsed["redhat_release"], @@ -380,8 +399,15 @@ def _introspect_image(self): } self._images.selected["python"] = parsed["python_packages"] self._images.selected["system"] = parsed["system_packages"] + except KeyError: + self._logger.exception( + "Image introspection failed (keys), the return value was: %s", output[0:1000] + ) + self.notify_failed() + return False + return True - def _parse(self, output) -> None: + def _parse(self, output) -> Union[Dict, None]: """parse the introspection output""" # pylint: disable=too-many-branches try: @@ -401,3 +427,11 @@ def _parse(self, output) -> None: for error in parsed["errors"]: self._logger.error("%s %s", error["path"], error["error"]) return parsed + + def notify_failed(self): + """notify introspection failed""" + msgs = ["humph. Something went really wrong while introspecting the image."] + msgs.append("Details have been added to the log file") + closing = ["[HINT] Please log an issue about this one, it shouldn't have happened"] + warning = warning_notification(messages=msgs + closing) + self._interaction.ui.show(warning) diff --git a/ansible_navigator/runner/api.py b/ansible_navigator/runner/api.py index 356d7653e..2b9890981 100644 --- a/ansible_navigator/runner/api.py +++ b/ansible_navigator/runner/api.py @@ -1,12 +1,12 @@ """ ansible_runner API's interface """ -import shutil -import sys import logging import os +import shutil +import tempfile +import sys from queue import Queue -from tempfile import gettempdir from typing import Any from typing import Dict from typing import List @@ -84,11 +84,9 @@ def __init__( It the timeout is triggered it will force cancel the execution. """ - if isinstance(private_data_dir, str): - self._private_data_dir = private_data_dir - else: - self._private_data_dir = os.path.join(gettempdir(), "ansible-navigator") + self._logger = logging.getLogger(__name__) + self._set_private_data_directory(private_data_dir) self._ce = container_engine self._ee = execution_environment self._eei = execution_environment_image @@ -106,7 +104,6 @@ def __init__( self.cancelled: bool = False self.finished: bool = False self.status: Optional[str] = None - self._logger = logging.getLogger(__name__) self._runner_args: Dict = {} self._runner_artifact_dir: Optional[str] = None if self._ee: @@ -162,6 +159,29 @@ class destructor, handle runner artifact file deletion is rotate_artifacts ) shutil.rmtree(self._runner_artifact_dir, ignore_errors=True) + @staticmethod + def _generate_tmp_directory(): + """generate a tmp directory for artifacts""" + return tempfile.mkdtemp(prefix="ansible-navigator_") + + def _set_private_data_directory(self, provided: Union[str, None]) -> None: + """Set the private data directory to the provided, the username, or a uuid""" + + if isinstance(provided, str): + if os.access(provided, os.W_OK | os.R_OK | os.X_OK): + private_data_directory = provided + source = "user provided" + else: + self._logger.debug("Provided private data dir `%s` was not user writable") + private_data_directory = self._generate_tmp_directory() + source = "user provided, but changed to tmp location due to permissions" + else: + private_data_directory = self._generate_tmp_directory() + source = "not user provided, used tmp location" + + self._private_data_dir = private_data_directory + self._logger.debug("private data dir %s: %s", source, self._private_data_dir) + def runner_artifacts_handler(self, artifact_dir): """ ansible-runner callback to handle artifacts after each runner innvocation diff --git a/setup.cfg b/setup.cfg index 92eaba942..b2a158c0c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -33,7 +33,7 @@ classifiers = [options] packages = find: install_requires = - ansible-runner >=2rc1,<3 + ansible-runner >=2,<3 jinja2 onigurumacffi >=1.1.0,<2 pyyaml