From 684f2f0eec78a46d263710a4332ce2f935813d0b Mon Sep 17 00:00:00 2001 From: cidrblock Date: Fri, 19 Nov 2021 10:10:33 -0800 Subject: [PATCH 01/59] Initial exec --- ansible-navigator.yaml | 25 ++++++ src/ansible_navigator/actions/_actions.py | 9 +- src/ansible_navigator/actions/exec.py | 88 +++++++++++++++++++ .../navigator_configuration.py | 26 ++++++ .../navigator_post_processor.py | 7 ++ .../ansible-navigator.yml | 9 +- tests/unit/configuration_subsystem/data.py | 13 +++ .../test_precedence.py | 13 +-- 8 files changed, 180 insertions(+), 10 deletions(-) create mode 100644 ansible-navigator.yaml create mode 100644 src/ansible_navigator/actions/exec.py diff --git a/ansible-navigator.yaml b/ansible-navigator.yaml new file mode 100644 index 000000000..6c279bc88 --- /dev/null +++ b/ansible-navigator.yaml @@ -0,0 +1,25 @@ +--- +ansible-navigator: + ansible: + inventories: + - inventory.yaml + + editor: + command: code -g {filename}:{line_number} + console: false + + execution-environment: + enabled: true + image: registry-proxy.engineering.redhat.com/rh-osbs/ansible-automation-platform-20-ee-supported-rhel8 + pull-policy: never + + logging: + level: debug + append: False + + playbook-artifact: + save-as: ./artifacts/{playbook_name}-artifact.json + + + + diff --git a/src/ansible_navigator/actions/_actions.py b/src/ansible_navigator/actions/_actions.py index bf1ded6f8..e24ddf130 100644 --- a/src/ansible_navigator/actions/_actions.py +++ b/src/ansible_navigator/actions/_actions.py @@ -3,6 +3,7 @@ import functools import importlib +import logging import re from collections import namedtuple @@ -19,6 +20,8 @@ import importlib_resources as resources # type: ignore[import, no-redef] +logger = logging.getLogger(__name__) + # Basic structure for storing information about one action ActionT = namedtuple("ActionT", ("name", "cls", "kegex")) @@ -95,7 +98,11 @@ def run_interactive(package: str, action: str, *args: Any, **_kwargs: Any) -> An """Call the given action's run""" action_cls = get(package, action) app, interaction = args - return action_cls(app.args).run(app=app, interaction=interaction) + if hasattr(action_cls(app.args), "run"): + return action_cls(app.args).run(app=app, interaction=interaction) + logger.error("Subcommand '%s' does not support mode interactive", action) + if hasattr(action_cls(app.args), "no_interactive_mode"): + return action_cls(app.args).no_interactive_mode(app=app, interaction=interaction) def run_interactive_factory(package: str) -> Callable: diff --git a/src/ansible_navigator/actions/exec.py b/src/ansible_navigator/actions/exec.py new file mode 100644 index 000000000..1da575fde --- /dev/null +++ b/src/ansible_navigator/actions/exec.py @@ -0,0 +1,88 @@ +""" :exec """ +import os + +from typing import Optional +from typing import Tuple +from typing import Union + +from . import _actions as actions +from ..app import App +from ..app_public import AppPublic +from ..runner.api import CommandRunner +from ..ui_framework import warning_notification +from ..ui_framework import Interaction + + +@actions.register +class Action(App): + """:exec""" + + # pylint: disable=too-few-public-methods + + KEGEX = r"^e(?:xec)?$" + + def __init__(self, args): + super().__init__(args=args, logger_name=__name__, name="exec") + + @staticmethod + def no_interactive_mode(interaction: Interaction, app: AppPublic) -> Union[Interaction, None]: + # pylint: disable=unused-argument + """Warm the user interactive mode is not supported""" + warning = warning_notification( + messages=[ + "The 'exec' subcommand is not available while using interactive mode.", + "[HINT] Start an additional instance of ansible-navigator in a new terminal with mode 'stdout'.", + " e.g. 'ansible-navigator exec --mode stdout", + ] + ) + interaction.ui.show(warning) + + def run_stdout(self) -> Union[None, int]: + """Run in mode stdout""" + self._logger.debug("exec requested in stdout mode") + response = self._run_runner() + if response: + _, _, ret_code = response + return ret_code + return None + + def _run_runner(self) -> Optional[Tuple]: + # pylint: disable=too-many-branches + # pylint: disable=too-many-statements + """spin up runner""" + + if isinstance(self._args.set_environment_variable, dict): + set_envvars = {**self._args.set_environment_variable} + else: + set_envvars = {} + + if self._args.display_color is False: + set_envvars["ANSIBLE_NOCOLOR"] = "1" + + kwargs = { + "container_engine": self._args.container_engine, + "host_cwd": os.getcwd(), + "execution_environment_image": self._args.execution_environment_image, + "execution_environment": self._args.execution_environment, + "navigator_mode": self._args.mode, + "pass_environment_variable": self._args.pass_environment_variable, + "set_environment_variable": set_envvars, + "timeout": self._args.ansible_runner_timeout, + } + + if isinstance(self._args.execution_environment_volume_mounts, list): + kwargs["container_volume_mounts"] = self._args.execution_environment_volume_mounts + + if isinstance(self._args.container_options, list): + kwargs["container_options"] = self._args.container_options + + if self._args.exec_shell and self._args.exec_command: + command = "/bin/bash" + pass_through_args = ["-c", self._args.exec_command] + kwargs.update({"cmdline": pass_through_args}) + else: + command = self._args.exec_command + + runner = CommandRunner(executable_cmd=command, **kwargs) + runner_return = runner.run() + return runner_return diff --git a/src/ansible_navigator/configuration_subsystem/navigator_configuration.py b/src/ansible_navigator/configuration_subsystem/navigator_configuration.py index b22a36b42..937bee5ce 100644 --- a/src/ansible_navigator/configuration_subsystem/navigator_configuration.py +++ b/src/ansible_navigator/configuration_subsystem/navigator_configuration.py @@ -116,6 +116,16 @@ class Internals(SimpleNamespace): " 'ansible-navigator doc --help-doc --mode stdout'" ), ), + SubCommand( + name="exec", + description="Run a command within an execution environment", + epilog=( + "Note: During development, it may become necessary to interact" + " directly with the execution environment to review and confirm" + " it's build and behavior. All navigator settings will be applied" + " when starting the execution environment." + ), + ), SubCommand( name="images", description="Explore execution environment images", @@ -246,6 +256,22 @@ class Internals(SimpleNamespace): short_description="Specify if the editor is console based", value=EntryValue(default=True), ), + Entry( + name="exec_command", + cli_parameters=CliParameters(short="--excmd"), + settings_file_path_override="exec.command", + short_description="Specify the command to run within the execution environment", + subcommands=["exec"], + value=EntryValue(default="/bin/bash"), + ), + Entry( + name="exec_shell", + cli_parameters=CliParameters(short="--exshell"), + settings_file_path_override="exec.shell", + short_description="Specify the exec command should be run in a shell. ('sh -c \"{exec_command}\"')", + subcommands=["exec"], + value=EntryValue(default=True), + ), Entry( name="execution_environment", choices=[True, False], diff --git a/src/ansible_navigator/configuration_subsystem/navigator_post_processor.py b/src/ansible_navigator/configuration_subsystem/navigator_post_processor.py index 9e6110f16..a658aef8a 100644 --- a/src/ansible_navigator/configuration_subsystem/navigator_post_processor.py +++ b/src/ansible_navigator/configuration_subsystem/navigator_post_processor.py @@ -310,6 +310,13 @@ def container_options(entry: Entry, config: ApplicationConfiguration) -> PostPro entry.value.current = flatten_list(entry.value.current) return messages, exit_messages + @_post_processor + def exec_shell(self, entry: Entry, config: ApplicationConfiguration) -> PostProcessorReturn: + # pylint: disable=unused-argument + """Post process help_config""" + messages, exit_messages = self._true_or_false(entry, config) + return messages, exit_messages + @_post_processor def help_config(self, entry: Entry, config: ApplicationConfiguration) -> PostProcessorReturn: # pylint: disable=unused-argument diff --git a/tests/fixtures/unit/configuration_subsystem/ansible-navigator.yml b/tests/fixtures/unit/configuration_subsystem/ansible-navigator.yml index 298a69f25..3b6ae3138 100644 --- a/tests/fixtures/unit/configuration_subsystem/ansible-navigator.yml +++ b/tests/fixtures/unit/configuration_subsystem/ansible-navigator.yml @@ -15,13 +15,16 @@ ansible-navigator: color: enable: False osc4: False - editor: - command: vim_from_setting - console: False documentation: plugin: name: shell type: become + editor: + command: vim_from_setting + console: False + exec: + shell: False + command: /bin/foo execution-environment: container-engine: podman enabled: False diff --git a/tests/unit/configuration_subsystem/data.py b/tests/unit/configuration_subsystem/data.py index 095757f27..50a620d93 100644 --- a/tests/unit/configuration_subsystem/data.py +++ b/tests/unit/configuration_subsystem/data.py @@ -99,6 +99,16 @@ def d2t(dyct): {"app": "doc", "plugin_name": "shell", "plugin_type": "become"}, ), ] +CLI_DATA_EXEC = [ + ( + "exec --mode stdout --excmd 'ls -l' --exshell False", + {"app": "exec", "exec_command": "ls -l", "exec_shell": False, "mode": "stdout"}, + ), + ( + "exec --mode stdout --exec-command 'ls -l' --exec-shell=False", + {"app": "exec", "exec_command": "ls -l", "exec_shell": False, "mode": "stdout"}, + ), +] CLI_DATA_INVENTORY = [ ("inventory -i /tmp/inv1.yml", {"app": "inventory", "inventory": ["/tmp/inv1.yml"]}), ( @@ -195,6 +205,7 @@ def cli_data(): CLI_DATA_COLLECTIONS # type: ignore + CLI_DATA_CONFIG # type: ignore + CLI_DATA_DOC # type: ignore + + CLI_DATA_EXEC # type: ignore + CLI_DATA_INVENTORY # type: ignore + CLI_DATA_INVENTORY_COLUMNS # type: ignore + CLI_DATA_REPLAY # type: ignore @@ -219,6 +230,8 @@ def cli_data(): ("display_color", "yellow is the color of a banana", False), ("editor_command", "nano_envvar", "nano_envvar"), ("editor_console", "false", False), + ("exec_command", "/bin/foo", "/bin/foo"), + ("exec_shell", "false", False), ("execution_environment", "false", False), ("execution_environment_image", "test_image:latest", "test_image:latest"), ("execution_environment_volume_mounts", "/test1:/test1:Z", ["/test1:/test1:Z"]), diff --git a/tests/unit/configuration_subsystem/test_precedence.py b/tests/unit/configuration_subsystem/test_precedence.py index 8da14a45b..b15f6338b 100644 --- a/tests/unit/configuration_subsystem/test_precedence.py +++ b/tests/unit/configuration_subsystem/test_precedence.py @@ -10,6 +10,7 @@ """ import os +import shlex from unittest import mock from unittest.mock import patch @@ -45,10 +46,10 @@ def test_all_entries_reflect_cli_given_envvars( ): """Ensure all entries are set by the CLI, even with environment variables set.""" if base is None: - params = cli_entry.split() + params = shlex.split(cli_entry) expected = dict(expected) else: - params = cli_entry.split() + " ".join(base.splitlines()).split() + params = shlex.split(cli_entry) + " ".join(base.splitlines()).split() expected = {**dict(expected), **dict(BASE_EXPECTED)} envvars = {} @@ -82,10 +83,10 @@ def test_all_entries_reflect_cli_given_settings( either DEFAULT_CFG or USER_CFG """ if base is None: - params = cli_entry.split() + params = shlex.split(cli_entry) expected = dict(expected) else: - params = cli_entry.split() + " ".join(base.splitlines()).split() + params = shlex.split(cli_entry) + " ".join(base.splitlines()).split() expected = {**dict(expected), **dict(BASE_EXPECTED)} response = generate_config(params=params, setting_file_name=settings) @@ -118,10 +119,10 @@ def test_all_entries_reflect_cli_given_settings_and_envars( even though an empty or full settings file was provided """ if base is None: - params = cli_entry.split() + params = shlex.split(cli_entry) expected = dict(expected) else: - params = cli_entry.split() + " ".join(base.splitlines()).split() + params = shlex.split(cli_entry) + " ".join(base.splitlines()).split() expected = {**dict(expected), **dict(BASE_EXPECTED)} envvars = {} From ee0a629afebd07ae2fe448a1d4f63836a410c761 Mon Sep 17 00:00:00 2001 From: cidrblock Date: Fri, 19 Nov 2021 10:10:54 -0800 Subject: [PATCH 02/59] rm test settings --- ansible-navigator.yaml | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 ansible-navigator.yaml diff --git a/ansible-navigator.yaml b/ansible-navigator.yaml deleted file mode 100644 index 6c279bc88..000000000 --- a/ansible-navigator.yaml +++ /dev/null @@ -1,25 +0,0 @@ ---- -ansible-navigator: - ansible: - inventories: - - inventory.yaml - - editor: - command: code -g {filename}:{line_number} - console: false - - execution-environment: - enabled: true - image: registry-proxy.engineering.redhat.com/rh-osbs/ansible-automation-platform-20-ee-supported-rhel8 - pull-policy: never - - logging: - level: debug - append: False - - playbook-artifact: - save-as: ./artifacts/{playbook_name}-artifact.json - - - - From 1472d5229c606b8a06b5a9539cf3419a1806b0bb Mon Sep 17 00:00:00 2001 From: cidrblock Date: Fri, 19 Nov 2021 10:30:13 -0800 Subject: [PATCH 03/59] Add choices for exec_shell --- .../configuration_subsystem/navigator_configuration.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ansible_navigator/configuration_subsystem/navigator_configuration.py b/src/ansible_navigator/configuration_subsystem/navigator_configuration.py index 937bee5ce..8e7616557 100644 --- a/src/ansible_navigator/configuration_subsystem/navigator_configuration.py +++ b/src/ansible_navigator/configuration_subsystem/navigator_configuration.py @@ -266,6 +266,7 @@ class Internals(SimpleNamespace): ), Entry( name="exec_shell", + choices=[True, False], cli_parameters=CliParameters(short="--exshell"), settings_file_path_override="exec.shell", short_description="Specify the exec command should be run in a shell. ('sh -c \"{exec_command}\"')", From ebd558e0d4c2879ffac0e4e196d390d93fb8b490 Mon Sep 17 00:00:00 2001 From: cidrblock Date: Fri, 19 Nov 2021 10:32:32 -0800 Subject: [PATCH 04/59] Update desc for exec shell to reflect bash --- .../configuration_subsystem/navigator_configuration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ansible_navigator/configuration_subsystem/navigator_configuration.py b/src/ansible_navigator/configuration_subsystem/navigator_configuration.py index 8e7616557..c43572054 100644 --- a/src/ansible_navigator/configuration_subsystem/navigator_configuration.py +++ b/src/ansible_navigator/configuration_subsystem/navigator_configuration.py @@ -269,7 +269,7 @@ class Internals(SimpleNamespace): choices=[True, False], cli_parameters=CliParameters(short="--exshell"), settings_file_path_override="exec.shell", - short_description="Specify the exec command should be run in a shell. ('sh -c \"{exec_command}\"')", + short_description="Specify the exec command should be run in a shell. ('/bin/bash -c \"{exec_command}\"')", subcommands=["exec"], value=EntryValue(default=True), ), From aa0751972628bcbb72c97ab54d3226f88a22add1 Mon Sep 17 00:00:00 2001 From: cidrblock Date: Fri, 19 Nov 2021 11:09:15 -0800 Subject: [PATCH 05/59] lint and require EE for exec --- src/ansible_navigator/actions/_actions.py | 1 + src/ansible_navigator/actions/exec.py | 3 ++- .../configuration_subsystem/navigator_configuration.py | 5 ++++- .../navigator_post_processor.py | 10 ++++++++++ 4 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/ansible_navigator/actions/_actions.py b/src/ansible_navigator/actions/_actions.py index e24ddf130..0b08a799d 100644 --- a/src/ansible_navigator/actions/_actions.py +++ b/src/ansible_navigator/actions/_actions.py @@ -103,6 +103,7 @@ def run_interactive(package: str, action: str, *args: Any, **_kwargs: Any) -> An logger.error("Subcommand '%s' does not support mode interactive", action) if hasattr(action_cls(app.args), "no_interactive_mode"): return action_cls(app.args).no_interactive_mode(app=app, interaction=interaction) + return None def run_interactive_factory(package: str) -> Callable: diff --git a/src/ansible_navigator/actions/exec.py b/src/ansible_navigator/actions/exec.py index 1da575fde..f4aa4373b 100644 --- a/src/ansible_navigator/actions/exec.py +++ b/src/ansible_navigator/actions/exec.py @@ -31,7 +31,8 @@ def no_interactive_mode(interaction: Interaction, app: AppPublic) -> Union[Inter warning = warning_notification( messages=[ "The 'exec' subcommand is not available while using interactive mode.", - "[HINT] Start an additional instance of ansible-navigator in a new terminal with mode 'stdout'.", + "[HINT] Start an additional instance of ansible-navigator" + " in a new terminal with mode 'stdout'.", " e.g. 'ansible-navigator exec --mode stdout", ] ) diff --git a/src/ansible_navigator/configuration_subsystem/navigator_configuration.py b/src/ansible_navigator/configuration_subsystem/navigator_configuration.py index c43572054..a7eb40224 100644 --- a/src/ansible_navigator/configuration_subsystem/navigator_configuration.py +++ b/src/ansible_navigator/configuration_subsystem/navigator_configuration.py @@ -269,7 +269,10 @@ class Internals(SimpleNamespace): choices=[True, False], cli_parameters=CliParameters(short="--exshell"), settings_file_path_override="exec.shell", - short_description="Specify the exec command should be run in a shell. ('/bin/bash -c \"{exec_command}\"')", + short_description=( + "Specify the exec command should be run in a shell." + " ('/bin/bash -c \"{exec_command}\"')" + ), subcommands=["exec"], value=EntryValue(default=True), ), diff --git a/src/ansible_navigator/configuration_subsystem/navigator_post_processor.py b/src/ansible_navigator/configuration_subsystem/navigator_post_processor.py index a658aef8a..21b51b8e8 100644 --- a/src/ansible_navigator/configuration_subsystem/navigator_post_processor.py +++ b/src/ansible_navigator/configuration_subsystem/navigator_post_processor.py @@ -208,6 +208,16 @@ def execution_environment(self, entry, config) -> PostProcessorReturn: new_messages, new_exit_messages = check_for_ansible() messages.extend(new_messages) exit_messages.extend(new_exit_messages) + + if config.app == "exec": + exit_msg = "The 'exec' subcommand requires execution environment support." + exit_messages.append(ExitMessage(message=exit_msg)) + hint = ( + f"Try again with '{entry.cli_parameters.short} true'" + " to enable the use of an execution environment." + ) + exit_messages.append(ExitMessage(message=hint, prefix=ExitPrefix.HINT)) + return messages, exit_messages @staticmethod From f593f27bab4bca202b0c3827e9b8d496914cf6da Mon Sep 17 00:00:00 2001 From: cidrblock Date: Fri, 19 Nov 2021 11:10:41 -0800 Subject: [PATCH 06/59] mypy fix, return in exec for warning --- src/ansible_navigator/actions/exec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ansible_navigator/actions/exec.py b/src/ansible_navigator/actions/exec.py index f4aa4373b..2647f9149 100644 --- a/src/ansible_navigator/actions/exec.py +++ b/src/ansible_navigator/actions/exec.py @@ -25,7 +25,7 @@ def __init__(self, args): super().__init__(args=args, logger_name=__name__, name="exec") @staticmethod - def no_interactive_mode(interaction: Interaction, app: AppPublic) -> Union[Interaction, None]: + def no_interactive_mode(interaction: Interaction, app: AppPublic) -> None: # pylint: disable=unused-argument """Warm the user interactive mode is not supported""" warning = warning_notification( From 21984cf705b24473647165e368183ccc11ad0ebb Mon Sep 17 00:00:00 2001 From: cidrblock Date: Fri, 19 Nov 2021 11:48:47 -0800 Subject: [PATCH 07/59] Remove exec unit test b/c it requries EE support --- tests/unit/configuration_subsystem/data.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/tests/unit/configuration_subsystem/data.py b/tests/unit/configuration_subsystem/data.py index 50a620d93..519fbba48 100644 --- a/tests/unit/configuration_subsystem/data.py +++ b/tests/unit/configuration_subsystem/data.py @@ -99,16 +99,6 @@ def d2t(dyct): {"app": "doc", "plugin_name": "shell", "plugin_type": "become"}, ), ] -CLI_DATA_EXEC = [ - ( - "exec --mode stdout --excmd 'ls -l' --exshell False", - {"app": "exec", "exec_command": "ls -l", "exec_shell": False, "mode": "stdout"}, - ), - ( - "exec --mode stdout --exec-command 'ls -l' --exec-shell=False", - {"app": "exec", "exec_command": "ls -l", "exec_shell": False, "mode": "stdout"}, - ), -] CLI_DATA_INVENTORY = [ ("inventory -i /tmp/inv1.yml", {"app": "inventory", "inventory": ["/tmp/inv1.yml"]}), ( @@ -205,7 +195,6 @@ def cli_data(): CLI_DATA_COLLECTIONS # type: ignore + CLI_DATA_CONFIG # type: ignore + CLI_DATA_DOC # type: ignore - + CLI_DATA_EXEC # type: ignore + CLI_DATA_INVENTORY # type: ignore + CLI_DATA_INVENTORY_COLUMNS # type: ignore + CLI_DATA_REPLAY # type: ignore From 2cc2b3f696b0b916fedabd30a39367739af1ee4d Mon Sep 17 00:00:00 2001 From: cidrblock Date: Tue, 30 Nov 2021 08:41:40 -0800 Subject: [PATCH 08/59] Initial stdout integration tests --- src/ansible_navigator/actions/exec.py | 9 +- .../actions/exec/ansible-navigator.yaml | 4 + .../test_stdout_config_cli.py/test/0.json | 19 ++++ .../test_stdout_config_cli.py/test/1.json | 23 +++++ .../test_stdout_config_cli.py/test/2.json | 16 ++++ .../test_stdout_config_cli.py/test/3.json | 16 ++++ .../test_stdout_config_file.py/test/0.json | 19 ++++ .../test_stdout_config_file.py/test/1.json | 16 ++++ tests/integration/actions/exec/__init__.py | 0 tests/integration/actions/exec/base.py | 90 +++++++++++++++++++ .../actions/exec/test_stdout_config_cli.py | 75 ++++++++++++++++ .../actions/exec/test_stdout_config_file.py | 60 +++++++++++++ 12 files changed, 345 insertions(+), 2 deletions(-) create mode 100644 tests/fixtures/integration/actions/exec/ansible-navigator.yaml create mode 100644 tests/fixtures/integration/actions/exec/test_stdout_config_cli.py/test/0.json create mode 100644 tests/fixtures/integration/actions/exec/test_stdout_config_cli.py/test/1.json create mode 100644 tests/fixtures/integration/actions/exec/test_stdout_config_cli.py/test/2.json create mode 100644 tests/fixtures/integration/actions/exec/test_stdout_config_cli.py/test/3.json create mode 100644 tests/fixtures/integration/actions/exec/test_stdout_config_file.py/test/0.json create mode 100644 tests/fixtures/integration/actions/exec/test_stdout_config_file.py/test/1.json create mode 100644 tests/integration/actions/exec/__init__.py create mode 100644 tests/integration/actions/exec/base.py create mode 100644 tests/integration/actions/exec/test_stdout_config_cli.py create mode 100644 tests/integration/actions/exec/test_stdout_config_file.py diff --git a/src/ansible_navigator/actions/exec.py b/src/ansible_navigator/actions/exec.py index 2647f9149..d8ecb237a 100644 --- a/src/ansible_navigator/actions/exec.py +++ b/src/ansible_navigator/actions/exec.py @@ -1,5 +1,6 @@ """ :exec """ import os +import shlex from typing import Optional from typing import Tuple @@ -80,9 +81,13 @@ def _run_runner(self) -> Optional[Tuple]: if self._args.exec_shell and self._args.exec_command: command = "/bin/bash" pass_through_args = ["-c", self._args.exec_command] - kwargs.update({"cmdline": pass_through_args}) + kwargs["cmdline"] = pass_through_args else: - command = self._args.exec_command + parts = shlex.split(self._args.exec_command) + command = parts[0] + if len(parts) > 1: + pass_through_args = parts[1:] + kwargs["cmdline"] = pass_through_args runner = CommandRunner(executable_cmd=command, **kwargs) runner_return = runner.run() diff --git a/tests/fixtures/integration/actions/exec/ansible-navigator.yaml b/tests/fixtures/integration/actions/exec/ansible-navigator.yaml new file mode 100644 index 000000000..9306f26a4 --- /dev/null +++ b/tests/fixtures/integration/actions/exec/ansible-navigator.yaml @@ -0,0 +1,4 @@ +ansible-navigator: + exec: + command: echo test_data_from_config + shell: False \ No newline at end of file diff --git a/tests/fixtures/integration/actions/exec/test_stdout_config_cli.py/test/0.json b/tests/fixtures/integration/actions/exec/test_stdout_config_cli.py/test/0.json new file mode 100644 index 000000000..88feda76f --- /dev/null +++ b/tests/fixtures/integration/actions/exec/test_stdout_config_cli.py/test/0.json @@ -0,0 +1,19 @@ +{ + "name": "test[exec echo with ee clear && ansible-navigator exec --excmd 'echo test_data_from_cli' --ee True --ll debug --mode stdout]", + "index": 0, + "comment": "exec echo with ee", + "additional_information": { + "look_fors": [ + "bash", + "test_data_from_cli" + ], + "look_nots": [ + "ERROR" + ], + "compared_fixture": false + }, + "output": [ + "test_data_from_cli", + "(venv) bash-5.1$" + ] +} \ No newline at end of file diff --git a/tests/fixtures/integration/actions/exec/test_stdout_config_cli.py/test/1.json b/tests/fixtures/integration/actions/exec/test_stdout_config_cli.py/test/1.json new file mode 100644 index 000000000..b0ea54819 --- /dev/null +++ b/tests/fixtures/integration/actions/exec/test_stdout_config_cli.py/test/1.json @@ -0,0 +1,23 @@ +{ + "name": "test[exec echo without ee clear && ansible-navigator exec --excmd 'echo test_data_from_cli' --ee False --ll debug --mode stdout]", + "index": 1, + "comment": "exec echo without ee", + "additional_information": { + "look_fors": [ + "bash", + "test_data_from_cli", + "ERROR", + "requires execution environment support" + ], + "look_nots": [], + "compared_fixture": false + }, + "output": [ + "[ERROR]: Command provided: 'exec --excmd 'echo test_data_from_cli' --ee False --ll debug --mode stdout'", + "[ERROR]: The 'exec' subcommand requires execution environment support.", + " [HINT]: Try again with '--ee true' to enable the use of an execution environment.", + "[ERROR]: Configuration failed, using default log file location: /home/user/github/ansible-navigator/ansible-navigator.log. Log level set to debug", + " [HINT]: Review the hints and log file to see what went wrong: /home/user/github/ansible-navigator/ansible-navigator.log", + "(venv) bash-5.1$" + ] +} \ No newline at end of file diff --git a/tests/fixtures/integration/actions/exec/test_stdout_config_cli.py/test/2.json b/tests/fixtures/integration/actions/exec/test_stdout_config_cli.py/test/2.json new file mode 100644 index 000000000..19065707a --- /dev/null +++ b/tests/fixtures/integration/actions/exec/test_stdout_config_cli.py/test/2.json @@ -0,0 +1,16 @@ +{ + "name": "test[exec echo check path via shell clear && ansible-navigator exec --excmd 'echo $PATH' --ee True --ll debug --mode stdout]", + "index": 2, + "comment": "exec echo check path via shell", + "additional_information": { + "look_fors": [ + "/sbin" + ], + "look_nots": [], + "compared_fixture": false + }, + "output": [ + "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "(venv) bash-5.1$" + ] +} \ No newline at end of file diff --git a/tests/fixtures/integration/actions/exec/test_stdout_config_cli.py/test/3.json b/tests/fixtures/integration/actions/exec/test_stdout_config_cli.py/test/3.json new file mode 100644 index 000000000..ce64ce6ac --- /dev/null +++ b/tests/fixtures/integration/actions/exec/test_stdout_config_cli.py/test/3.json @@ -0,0 +1,16 @@ +{ + "name": "test[exec echo check no path via shell clear && ansible-navigator exec --excmd 'echo $PATH' --exshell false --ee True --ll debug --mode stdout]", + "index": 3, + "comment": "exec echo check no path via shell", + "additional_information": { + "look_fors": [ + "$PATH" + ], + "look_nots": [], + "compared_fixture": false + }, + "output": [ + "$PATH", + "(venv) bash-5.1$" + ] +} \ No newline at end of file diff --git a/tests/fixtures/integration/actions/exec/test_stdout_config_file.py/test/0.json b/tests/fixtures/integration/actions/exec/test_stdout_config_file.py/test/0.json new file mode 100644 index 000000000..5595de128 --- /dev/null +++ b/tests/fixtures/integration/actions/exec/test_stdout_config_file.py/test/0.json @@ -0,0 +1,19 @@ +{ + "name": "test[exec echo with ee clear && ansible-navigator exec --ee True --ll debug --mode stdout]", + "index": 0, + "comment": "exec echo with ee", + "additional_information": { + "look_fors": [ + "bash", + "test_data_from_config" + ], + "look_nots": [ + "ERROR" + ], + "compared_fixture": false + }, + "output": [ + "test_data_from_config", + "(venv) bash-5.1$" + ] +} \ No newline at end of file diff --git a/tests/fixtures/integration/actions/exec/test_stdout_config_file.py/test/1.json b/tests/fixtures/integration/actions/exec/test_stdout_config_file.py/test/1.json new file mode 100644 index 000000000..b4c374ab9 --- /dev/null +++ b/tests/fixtures/integration/actions/exec/test_stdout_config_file.py/test/1.json @@ -0,0 +1,16 @@ +{ + "name": "test[exec echo check no path via shell clear && ansible-navigator exec --excmd '/usr/bin/echo $PATH' --ee True --ll debug --mode stdout]", + "index": 1, + "comment": "exec echo check no path via shell", + "additional_information": { + "look_fors": [], + "look_nots": [ + "/sbin" + ], + "compared_fixture": false + }, + "output": [ + "$PATH", + "(venv) bash-5.1$" + ] +} \ No newline at end of file diff --git a/tests/integration/actions/exec/__init__.py b/tests/integration/actions/exec/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/integration/actions/exec/base.py b/tests/integration/actions/exec/base.py new file mode 100644 index 000000000..fe0737381 --- /dev/null +++ b/tests/integration/actions/exec/base.py @@ -0,0 +1,90 @@ +""" base class for exec interactive and stdout tests +""" +import difflib +import json +import os + +import pytest + +from ..._common import fixture_path_from_request +from ..._common import update_fixtures +from ..._tmux_session import TmuxSession +from ..._interactions import SearchFor +from ..._interactions import Step +from ....defaults import FIXTURES_DIR + +TEST_FIXTURE_DIR = os.path.join(FIXTURES_DIR, "integration", "actions", "exec") +TEST_CONFIG_FILE = os.path.join(TEST_FIXTURE_DIR, "ansible-navigator.yaml") + + +class BaseClass: + """base class for interactive/stdout exec tests""" + + UPDATE_FIXTURES = False + PANE_HEIGHT = 25 + PANE_WIDTH = 300 + CONFIG_FILE = None + + @pytest.fixture(scope="module", name="tmux_session") + def fixture_tmux_session(self, request): + """tmux fixture for this module""" + params = { + "unique_test_id": request.node.nodeid, + "pane_height": self.PANE_HEIGHT, + "pane_width": self.PANE_WIDTH, + } + if isinstance(self.CONFIG_FILE, str): + assert os.path.exists(self.CONFIG_FILE) + params["config_path"] = self.CONFIG_FILE + + with TmuxSession(**params) as tmux_session: + yield tmux_session + + def test(self, request, tmux_session, step): + # pylint:disable=unused-argument + # pylint: disable=too-few-public-methods + # pylint: disable=too-many-arguments + """test interactive/stdout exec""" + + if step.search_within_response is SearchFor.PROMPT: + search_within_response = tmux_session.cli_prompt + else: + raise ValueError("test mode not set") + + received_output = tmux_session.interaction( + value=step.user_input, + search_within_response=search_within_response, + ) + + if ( + self.UPDATE_FIXTURES + or os.environ.get("ANSIBLE_NAVIGATOR_UPDATE_TEST_FIXTURES") == "true" + ): + update_fixtures( + request, + step.step_index, + received_output, + step.comment, + additional_information={ + "look_fors": step.look_fors, + "look_nots": step.look_nots, + "compared_fixture": not any((step.look_fors, step.look_nots)), + }, + ) + + page = " ".join(received_output) + + if step.look_fors: + assert all(look_for in page for look_for in step.look_fors) + + if step.look_nots: + assert not any(look_not in page for look_not in step.look_nots) + + if not any((step.look_fors, step.look_nots)): + dir_path, file_name = fixture_path_from_request(request, step.step_index) + with open(f"{dir_path}/{file_name}") as infile: + expected_output = json.load(infile)["output"] + + assert expected_output == received_output, "\n" + "\n".join( + difflib.unified_diff(expected_output, received_output, "expected", "received") + ) diff --git a/tests/integration/actions/exec/test_stdout_config_cli.py b/tests/integration/actions/exec/test_stdout_config_cli.py new file mode 100644 index 000000000..f7cfd21c8 --- /dev/null +++ b/tests/integration/actions/exec/test_stdout_config_cli.py @@ -0,0 +1,75 @@ +""" exec stdout tests using tmux """ +import pytest + + +from .base import BaseClass + +from ..._interactions import Command +from ..._interactions import SearchFor +from ..._interactions import Step +from ..._interactions import add_indicies + + +class StdoutCommand(Command): + """stdout command""" + + mode = "stdout" + subcommand = "exec" + preclear = True + + +class ShellCommand(Step): + """a shell command""" + + search_within_response = SearchFor.PROMPT + + +stdout_tests = ( + ShellCommand( + comment="exec echo with ee", + user_input=StdoutCommand( + cmdline="--excmd 'echo test_data_from_cli'", + execution_environment=True, + ).join(), + look_fors=["bash", "test_data_from_cli"], + look_nots=["ERROR"], + ), + ShellCommand( + comment="exec echo without ee", + user_input=StdoutCommand( + cmdline="--excmd 'echo test_data_from_cli'", + execution_environment=False, + ).join(), + look_fors=["bash", "test_data_from_cli", "ERROR", "requires execution environment support"], + ), + ShellCommand( + comment="exec echo check path via shell", + user_input=StdoutCommand( + cmdline="--excmd 'echo $PATH'", + execution_environment=True, + ).join(), + look_fors=["/sbin"], + ), + ShellCommand( + comment="exec echo check no path via shell", + user_input=StdoutCommand( + cmdline="--excmd 'echo $PATH' --exshell false", + execution_environment=True, + ).join(), + look_fors=["$PATH"], + ), +) + +steps = add_indicies(stdout_tests) + + +def step_id(value): + """return the test id from the test step object""" + return f"{value.comment} {value.user_input}" + + +@pytest.mark.parametrize("step", steps, ids=step_id) +class Test(BaseClass): + """run the tests""" + + UPDATE_FIXTURES = True diff --git a/tests/integration/actions/exec/test_stdout_config_file.py b/tests/integration/actions/exec/test_stdout_config_file.py new file mode 100644 index 000000000..7aaab9ce4 --- /dev/null +++ b/tests/integration/actions/exec/test_stdout_config_file.py @@ -0,0 +1,60 @@ +""" exec stdout tests using tmux """ +import pytest + + +from .base import BaseClass +from .base import TEST_CONFIG_FILE + +from ..._interactions import Command +from ..._interactions import SearchFor +from ..._interactions import Step +from ..._interactions import add_indicies + + +class StdoutCommand(Command): + """stdout command""" + + mode = "stdout" + subcommand = "exec" + preclear = True + + +class ShellCommand(Step): + """a shell command""" + + search_within_response = SearchFor.PROMPT + + +stdout_tests = ( + ShellCommand( + comment="exec echo with ee", + user_input=StdoutCommand( + execution_environment=True, + ).join(), + look_fors=["bash", "test_data_from_config"], + look_nots=["ERROR"], + ), + ShellCommand( + comment="exec echo check no path via shell", + user_input=StdoutCommand( + cmdline="--excmd '/usr/bin/echo $PATH'", + execution_environment=True, + ).join(), + look_nots=["/sbin"], + ), +) + +steps = add_indicies(stdout_tests) + + +def step_id(value): + """return the test id from the test step object""" + return f"{value.comment} {value.user_input}" + + +@pytest.mark.parametrize("step", steps, ids=step_id) +class Test(BaseClass): + """run the tests""" + + CONFIG_FILE = TEST_CONFIG_FILE + UPDATE_FIXTURES = True From ff23ccc0915adfc8d53949443e2fac738566b354 Mon Sep 17 00:00:00 2001 From: cidrblock Date: Tue, 30 Nov 2021 08:42:55 -0800 Subject: [PATCH 09/59] Initial integration tests --- tests/integration/actions/exec/test_stdout_config_cli.py | 2 +- tests/integration/actions/exec/test_stdout_config_file.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/actions/exec/test_stdout_config_cli.py b/tests/integration/actions/exec/test_stdout_config_cli.py index f7cfd21c8..a51422f24 100644 --- a/tests/integration/actions/exec/test_stdout_config_cli.py +++ b/tests/integration/actions/exec/test_stdout_config_cli.py @@ -72,4 +72,4 @@ def step_id(value): class Test(BaseClass): """run the tests""" - UPDATE_FIXTURES = True + UPDATE_FIXTURES = False diff --git a/tests/integration/actions/exec/test_stdout_config_file.py b/tests/integration/actions/exec/test_stdout_config_file.py index 7aaab9ce4..7df05861c 100644 --- a/tests/integration/actions/exec/test_stdout_config_file.py +++ b/tests/integration/actions/exec/test_stdout_config_file.py @@ -57,4 +57,4 @@ class Test(BaseClass): """run the tests""" CONFIG_FILE = TEST_CONFIG_FILE - UPDATE_FIXTURES = True + UPDATE_FIXTURES = False From b90945b37f34a20d1afa27a3f39be4e09956a0f4 Mon Sep 17 00:00:00 2001 From: cidrblock Date: Tue, 30 Nov 2021 09:52:39 -0800 Subject: [PATCH 10/59] Update for runner directory work --- src/ansible_navigator/actions/exec.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ansible_navigator/actions/exec.py b/src/ansible_navigator/actions/exec.py index d8ecb237a..969a97a56 100644 --- a/src/ansible_navigator/actions/exec.py +++ b/src/ansible_navigator/actions/exec.py @@ -9,7 +9,7 @@ from . import _actions as actions from ..app import App from ..app_public import AppPublic -from ..runner.api import CommandRunner +from ..runner import Command from ..ui_framework import warning_notification from ..ui_framework import Interaction @@ -89,6 +89,6 @@ def _run_runner(self) -> Optional[Tuple]: pass_through_args = parts[1:] kwargs["cmdline"] = pass_through_args - runner = CommandRunner(executable_cmd=command, **kwargs) + runner = Command(executable_cmd=command, **kwargs) runner_return = runner.run() return runner_return From 960c5ed2f8fe4557e7265c8ef47011040d62283a Mon Sep 17 00:00:00 2001 From: cidrblock Date: Tue, 30 Nov 2021 13:25:31 -0800 Subject: [PATCH 11/59] Move warning to base class, unit tests for command and params --- src/ansible_navigator/actions/exec.py | 40 +++++-------- src/ansible_navigator/app.py | 15 +++++ tests/unit/actions/test_exec.py | 86 +++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 24 deletions(-) create mode 100644 tests/unit/actions/test_exec.py diff --git a/src/ansible_navigator/actions/exec.py b/src/ansible_navigator/actions/exec.py index 969a97a56..b7ef7868f 100644 --- a/src/ansible_navigator/actions/exec.py +++ b/src/ansible_navigator/actions/exec.py @@ -8,10 +8,7 @@ from . import _actions as actions from ..app import App -from ..app_public import AppPublic from ..runner import Command -from ..ui_framework import warning_notification -from ..ui_framework import Interaction @actions.register @@ -26,18 +23,18 @@ def __init__(self, args): super().__init__(args=args, logger_name=__name__, name="exec") @staticmethod - def no_interactive_mode(interaction: Interaction, app: AppPublic) -> None: - # pylint: disable=unused-argument - """Warm the user interactive mode is not supported""" - warning = warning_notification( - messages=[ - "The 'exec' subcommand is not available while using interactive mode.", - "[HINT] Start an additional instance of ansible-navigator" - " in a new terminal with mode 'stdout'.", - " e.g. 'ansible-navigator exec --mode stdout", - ] - ) - interaction.ui.show(warning) + def _generate_command(exec_command: str, exec_shell: bool) -> Tuple: + """Generate the command and args""" + pass_through_args = None + if exec_shell and exec_command: + command = "/bin/bash" + pass_through_args = ["-c", exec_command] + else: + parts = shlex.split(exec_command) + command = parts[0] + if len(parts) > 1: + pass_through_args = parts[1:] + return (command, pass_through_args) def run_stdout(self) -> Union[None, int]: """Run in mode stdout""" @@ -78,16 +75,11 @@ def _run_runner(self) -> Optional[Tuple]: if isinstance(self._args.container_options, list): kwargs["container_options"] = self._args.container_options - if self._args.exec_shell and self._args.exec_command: - command = "/bin/bash" - pass_through_args = ["-c", self._args.exec_command] + command, pass_through_args = self._generate_command( + exec_command=self._args.exec_command, exec_shell=self._args.exec_shell + ) + if isinstance(pass_through_args, list): kwargs["cmdline"] = pass_through_args - else: - parts = shlex.split(self._args.exec_command) - command = parts[0] - if len(parts) > 1: - pass_through_args = parts[1:] - kwargs["cmdline"] = pass_through_args runner = Command(executable_cmd=command, **kwargs) runner_return = runner.run() diff --git a/src/ansible_navigator/app.py b/src/ansible_navigator/app.py index d814e1c86..ec27d902d 100644 --- a/src/ansible_navigator/app.py +++ b/src/ansible_navigator/app.py @@ -20,6 +20,8 @@ from .ui_framework import Interaction from .ui_framework import ui +from .ui_framework import warning_notification + from .utils import LogMessage from .utils import ExitMessage @@ -83,6 +85,19 @@ def app(self) -> AppPublic: ) raise AttributeError("app passed without args initialized") + def no_interactive_mode(self, interaction: Interaction, app: AppPublic) -> None: + # pylint: disable=unused-argument + """Warm the user interactive mode is not supported""" + warning = warning_notification( + messages=[ + f"The '{self._name}' subcommand is not available while using interactive mode.", + "[HINT] Start an additional instance of ansible-navigator" + " in a new terminal with mode 'stdout'.", + f" e.g. 'ansible-navigator {self._name} --mode stdout", + ] + ) + interaction.ui.show(warning) + @staticmethod def _copy_args(args: ApplicationConfiguration) -> ApplicationConfiguration: """Deepcopy the args. diff --git a/tests/unit/actions/test_exec.py b/tests/unit/actions/test_exec.py new file mode 100644 index 000000000..2fab7eb1d --- /dev/null +++ b/tests/unit/actions/test_exec.py @@ -0,0 +1,86 @@ +"""Some simple tests for exec command and param generation +""" + + +from typing import NamedTuple + +import pytest + +from ansible_navigator.actions.exec import Action as exec_action + + +class CommandTestData(NamedTuple): + """the artifact files test data object""" + + name: str + command: str + shell: bool + result_command: str + result_params: list + + +def id_from_data(value): + """return the name from the test data object""" + return f" {value.name} " + + +command_test_data = [ + CommandTestData( + name="With shell simple", + command="echo foo", + shell=True, + result_command="/bin/bash", + result_params=["-c", "echo foo"], + ), + CommandTestData( + name="Without shell simple", + command="echo foo", + shell=False, + result_command="echo", + result_params=["foo"], + ), + CommandTestData( + name="With shell complex", + command=( + "ansible-vault encrypt_string --vault-password-file" + " a_password_file 'foobar' --name 'the_secret'" + ), + shell=True, + result_command="/bin/bash", + result_params=[ + "-c", + "ansible-vault encrypt_string --vault-password-file" + " a_password_file 'foobar' --name 'the_secret'", + ], + ), + CommandTestData( + name="Without shell complex", + command=( + "ansible-vault encrypt_string --vault-password-file" + " a_password_file 'foobar' --name 'the secret'" + ), + shell=False, + result_command="ansible-vault", + result_params=[ + "encrypt_string", + "--vault-password-file", + "a_password_file", + "foobar", + "--name", + "the secret", + ], + ), +] + + +@pytest.mark.parametrize("data", command_test_data, ids=id_from_data) +def test_artifact_path(data): + """Test the generation of the command and params""" + + # pylint: disable=protected-access + command, params = exec_action._generate_command( + exec_command=data.command, exec_shell=data.shell + ) + comment = data, command, params + assert command == data.result_command, comment + assert params == data.result_params, comment From c3228c606a4745abf241a7330e533055830973f0 Mon Sep 17 00:00:00 2001 From: cidrblock Date: Wed, 1 Dec 2021 05:36:37 -0800 Subject: [PATCH 12/59] relrod PR review feedback --- src/ansible_navigator/actions/_actions.py | 4 +--- src/ansible_navigator/app.py | 2 +- .../configuration_subsystem/navigator_configuration.py | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/ansible_navigator/actions/_actions.py b/src/ansible_navigator/actions/_actions.py index 0b08a799d..bfd93ccfa 100644 --- a/src/ansible_navigator/actions/_actions.py +++ b/src/ansible_navigator/actions/_actions.py @@ -101,9 +101,7 @@ def run_interactive(package: str, action: str, *args: Any, **_kwargs: Any) -> An if hasattr(action_cls(app.args), "run"): return action_cls(app.args).run(app=app, interaction=interaction) logger.error("Subcommand '%s' does not support mode interactive", action) - if hasattr(action_cls(app.args), "no_interactive_mode"): - return action_cls(app.args).no_interactive_mode(app=app, interaction=interaction) - return None + return action_cls(app.args).no_interactive_mode(app=app, interaction=interaction) def run_interactive_factory(package: str) -> Callable: diff --git a/src/ansible_navigator/app.py b/src/ansible_navigator/app.py index ec27d902d..1420e6b22 100644 --- a/src/ansible_navigator/app.py +++ b/src/ansible_navigator/app.py @@ -87,7 +87,7 @@ def app(self) -> AppPublic: def no_interactive_mode(self, interaction: Interaction, app: AppPublic) -> None: # pylint: disable=unused-argument - """Warm the user interactive mode is not supported""" + """Warn the user interactive mode is not supported""" warning = warning_notification( messages=[ f"The '{self._name}' subcommand is not available while using interactive mode.", diff --git a/src/ansible_navigator/configuration_subsystem/navigator_configuration.py b/src/ansible_navigator/configuration_subsystem/navigator_configuration.py index a7eb40224..7e9ff10ef 100644 --- a/src/ansible_navigator/configuration_subsystem/navigator_configuration.py +++ b/src/ansible_navigator/configuration_subsystem/navigator_configuration.py @@ -122,7 +122,7 @@ class Internals(SimpleNamespace): epilog=( "Note: During development, it may become necessary to interact" " directly with the execution environment to review and confirm" - " it's build and behavior. All navigator settings will be applied" + " its build and behavior. All navigator settings will be applied" " when starting the execution environment." ), ), @@ -271,7 +271,6 @@ class Internals(SimpleNamespace): settings_file_path_override="exec.shell", short_description=( "Specify the exec command should be run in a shell." - " ('/bin/bash -c \"{exec_command}\"')" ), subcommands=["exec"], value=EntryValue(default=True), From 8fd5a08f452a541f2ecd56b4dd29fdde9377cbdd Mon Sep 17 00:00:00 2001 From: cidrblock Date: Sun, 5 Dec 2021 08:10:55 -0800 Subject: [PATCH 13/59] Update docstrings --- src/ansible_navigator/actions/exec.py | 30 ++++++++++++++----- .../navigator_configuration.py | 4 +-- src/ansible_navigator/utils.py | 4 +++ tests/integration/actions/exec/__init__.py | 1 + tests/integration/actions/exec/base.py | 27 ++++++++++++----- .../actions/exec/test_stdout_config_cli.py | 18 ++++++----- .../actions/exec/test_stdout_config_file.py | 17 +++++++---- tests/unit/actions/test_exec.py | 25 +++++++++------- 8 files changed, 85 insertions(+), 41 deletions(-) diff --git a/src/ansible_navigator/actions/exec.py b/src/ansible_navigator/actions/exec.py index b7ef7868f..7f18bbdc9 100644 --- a/src/ansible_navigator/actions/exec.py +++ b/src/ansible_navigator/actions/exec.py @@ -1,30 +1,41 @@ -""" :exec """ +"""Run the :exec subcommand.""" import os import shlex +from typing import List from typing import Optional from typing import Tuple from typing import Union from . import _actions as actions from ..app import App +from ..configuration_subsystem import ApplicationConfiguration from ..runner import Command @actions.register class Action(App): - """:exec""" + """Run the :exec subcommand.""" # pylint: disable=too-few-public-methods KEGEX = r"^e(?:xec)?$" - def __init__(self, args): + def __init__(self, args: ApplicationConfiguration): + """Initialize the action. + + :param args: The current application configuration. + """ super().__init__(args=args, logger_name=__name__, name="exec") @staticmethod - def _generate_command(exec_command: str, exec_shell: bool) -> Tuple: - """Generate the command and args""" + def _generate_command(exec_command: str, exec_shell: bool) -> Tuple[str, Optional[List[str]]]: + """Generate the command and args. + + :param exec_command: The command to run + :param exec_shell: Should the command be wrapped in a shell + :returns: The command and any pass through arguments + """ pass_through_args = None if exec_shell and exec_command: command = "/bin/bash" @@ -37,7 +48,10 @@ def _generate_command(exec_command: str, exec_shell: bool) -> Tuple: return (command, pass_through_args) def run_stdout(self) -> Union[None, int]: - """Run in mode stdout""" + """Run in mode stdout. + + :returns: The return code or None + """ self._logger.debug("exec requested in stdout mode") response = self._run_runner() if response: @@ -48,8 +62,10 @@ def run_stdout(self) -> Union[None, int]: def _run_runner(self) -> Optional[Tuple]: # pylint: disable=too-many-branches # pylint: disable=too-many-statements - """spin up runner""" + """Spin up runner. + :return: The stdout, stderr and return code from runner + """ if isinstance(self._args.set_environment_variable, dict): set_envvars = {**self._args.set_environment_variable} else: diff --git a/src/ansible_navigator/configuration_subsystem/navigator_configuration.py b/src/ansible_navigator/configuration_subsystem/navigator_configuration.py index 7e9ff10ef..4c7d93fef 100644 --- a/src/ansible_navigator/configuration_subsystem/navigator_configuration.py +++ b/src/ansible_navigator/configuration_subsystem/navigator_configuration.py @@ -269,9 +269,7 @@ class Internals(SimpleNamespace): choices=[True, False], cli_parameters=CliParameters(short="--exshell"), settings_file_path_override="exec.shell", - short_description=( - "Specify the exec command should be run in a shell." - ), + short_description="Specify the exec command should be run in a shell.", subcommands=["exec"], value=EntryValue(default=True), ), diff --git a/src/ansible_navigator/utils.py b/src/ansible_navigator/utils.py index ce9f8d319..4d655e10b 100644 --- a/src/ansible_navigator/utils.py +++ b/src/ansible_navigator/utils.py @@ -349,7 +349,11 @@ def divmod_int(numerator: Union[int, float], denominator: Union[int, float]) -> return int(quotient), int(remainder) +<<<<<<< HEAD def human_time(seconds: Union[int, float]) -> str: +======= +def human_time(seconds: int) -> str: +>>>>>>> 7eb4ca9 (Update docstrings) """Convert seconds into human readable 00d00h00m00s format.""" sign_string = "-" if seconds < 0 else "" seconds = abs(int(seconds)) diff --git a/tests/integration/actions/exec/__init__.py b/tests/integration/actions/exec/__init__.py index e69de29bb..e90e5f8c3 100644 --- a/tests/integration/actions/exec/__init__.py +++ b/tests/integration/actions/exec/__init__.py @@ -0,0 +1 @@ +"""Integration tests for the exec subcommand.""" diff --git a/tests/integration/actions/exec/base.py b/tests/integration/actions/exec/base.py index fe0737381..b6a45b49a 100644 --- a/tests/integration/actions/exec/base.py +++ b/tests/integration/actions/exec/base.py @@ -1,9 +1,11 @@ -""" base class for exec interactive and stdout tests -""" +"""The base class for exec interactive and stdout tests.""" + import difflib import json import os +from typing import Generator + import pytest from ..._common import fixture_path_from_request @@ -18,7 +20,7 @@ class BaseClass: - """base class for interactive/stdout exec tests""" + """The base class for interactive/stdout exec tests.""" UPDATE_FIXTURES = False PANE_HEIGHT = 25 @@ -26,8 +28,14 @@ class BaseClass: CONFIG_FILE = None @pytest.fixture(scope="module", name="tmux_session") - def fixture_tmux_session(self, request): - """tmux fixture for this module""" + def fixture_tmux_session( + self, request: pytest.FixtureRequest + ) -> Generator[TmuxSession, None, None]: + """Tmux fixture for this module. + + :param request: The request for this fixture + :yields: A tmux session + """ params = { "unique_test_id": request.node.nodeid, "pane_height": self.PANE_HEIGHT, @@ -40,12 +48,17 @@ def fixture_tmux_session(self, request): with TmuxSession(**params) as tmux_session: yield tmux_session - def test(self, request, tmux_session, step): + def test(self, request: pytest.FixtureRequest, tmux_session: TmuxSession, step: Step): # pylint:disable=unused-argument # pylint: disable=too-few-public-methods # pylint: disable=too-many-arguments - """test interactive/stdout exec""" + """Test interactive/stdout exec. + :param request: The test request + :param tmux_session: The tmux session + :param step: A step within a series of tests + :raises ValueError: If test mode isn't set + """ if step.search_within_response is SearchFor.PROMPT: search_within_response = tmux_session.cli_prompt else: diff --git a/tests/integration/actions/exec/test_stdout_config_cli.py b/tests/integration/actions/exec/test_stdout_config_cli.py index a51422f24..9be5de1b8 100644 --- a/tests/integration/actions/exec/test_stdout_config_cli.py +++ b/tests/integration/actions/exec/test_stdout_config_cli.py @@ -1,6 +1,6 @@ -""" exec stdout tests using tmux """ -import pytest +"""Tests for exec, mode stdout, parameters set using cli.""" +import pytest from .base import BaseClass @@ -11,7 +11,7 @@ class StdoutCommand(Command): - """stdout command""" + """A stdout command.""" mode = "stdout" subcommand = "exec" @@ -19,7 +19,7 @@ class StdoutCommand(Command): class ShellCommand(Step): - """a shell command""" + """A shell command.""" search_within_response = SearchFor.PROMPT @@ -63,13 +63,17 @@ class ShellCommand(Step): steps = add_indicies(stdout_tests) -def step_id(value): - """return the test id from the test step object""" +def step_id(value: ShellCommand) -> str: + """Return the test id from the test step object. + + :param value: The data fro the test iteration + :returns: An id for the test + """ return f"{value.comment} {value.user_input}" @pytest.mark.parametrize("step", steps, ids=step_id) class Test(BaseClass): - """run the tests""" + """Run the tests for exec, mode stdout, parameters set using cli.""" UPDATE_FIXTURES = False diff --git a/tests/integration/actions/exec/test_stdout_config_file.py b/tests/integration/actions/exec/test_stdout_config_file.py index 7df05861c..2f7ebb6c5 100644 --- a/tests/integration/actions/exec/test_stdout_config_file.py +++ b/tests/integration/actions/exec/test_stdout_config_file.py @@ -1,4 +1,5 @@ -""" exec stdout tests using tmux """ +"""Tests for exec, mode stdout, parameters set using config file.""" + import pytest @@ -12,7 +13,7 @@ class StdoutCommand(Command): - """stdout command""" + """A stdout command.""" mode = "stdout" subcommand = "exec" @@ -20,7 +21,7 @@ class StdoutCommand(Command): class ShellCommand(Step): - """a shell command""" + """A shell command.""" search_within_response = SearchFor.PROMPT @@ -47,14 +48,18 @@ class ShellCommand(Step): steps = add_indicies(stdout_tests) -def step_id(value): - """return the test id from the test step object""" +def step_id(value: ShellCommand) -> str: + """Return the test id from the test step object. + + :param value: The data fro the test iteration + :returns: An id for the test + """ return f"{value.comment} {value.user_input}" @pytest.mark.parametrize("step", steps, ids=step_id) class Test(BaseClass): - """run the tests""" + """Run the tests for exec, mode stdout, parameters set using config file.""" CONFIG_FILE = TEST_CONFIG_FILE UPDATE_FIXTURES = False diff --git a/tests/unit/actions/test_exec.py b/tests/unit/actions/test_exec.py index 2fab7eb1d..7f5eee945 100644 --- a/tests/unit/actions/test_exec.py +++ b/tests/unit/actions/test_exec.py @@ -1,16 +1,15 @@ -"""Some simple tests for exec command and param generation -""" - +"""Some simple tests for exec command and param generation.""" +from typing import List from typing import NamedTuple import pytest -from ansible_navigator.actions.exec import Action as exec_action +from ansible_navigator.actions.exec import Action as ExecAction class CommandTestData(NamedTuple): - """the artifact files test data object""" + """The artifact files test data object.""" name: str command: str @@ -20,7 +19,11 @@ class CommandTestData(NamedTuple): def id_from_data(value): - """return the name from the test data object""" + """Return the name from the test data object. + + :param value: The value from which the test id will be extracted + :returns: The test id + """ return f" {value.name} " @@ -74,13 +77,13 @@ def id_from_data(value): @pytest.mark.parametrize("data", command_test_data, ids=id_from_data) -def test_artifact_path(data): - """Test the generation of the command and params""" +def test_artifact_path(data: CommandTestData): + """Test the generation of the command and params. + :param data: The test data + """ # pylint: disable=protected-access - command, params = exec_action._generate_command( - exec_command=data.command, exec_shell=data.shell - ) + command, params = ExecAction._generate_command(exec_command=data.command, exec_shell=data.shell) comment = data, command, params assert command == data.result_command, comment assert params == data.result_params, comment From 37d333d683b850f7d19a508b7817d115074a5326 Mon Sep 17 00:00:00 2001 From: cidrblock Date: Wed, 8 Dec 2021 15:50:22 -0800 Subject: [PATCH 14/59] Add changes --- docs/changelog-fragments.d/526.feature.md | 2 ++ .../configuration_subsystem/navigator_post_processor.py | 7 ++++++- src/ansible_navigator/utils.py | 4 ---- tests/integration/actions/exec/test_stdout_config_cli.py | 2 +- tests/integration/actions/exec/test_stdout_config_file.py | 2 +- 5 files changed, 10 insertions(+), 7 deletions(-) create mode 100644 docs/changelog-fragments.d/526.feature.md diff --git a/docs/changelog-fragments.d/526.feature.md b/docs/changelog-fragments.d/526.feature.md new file mode 100644 index 000000000..5ec131ae1 --- /dev/null +++ b/docs/changelog-fragments.d/526.feature.md @@ -0,0 +1,2 @@ +Add ability to run commands directly within an execution environment. +-- by {user}`cidrblock` diff --git a/src/ansible_navigator/configuration_subsystem/navigator_post_processor.py b/src/ansible_navigator/configuration_subsystem/navigator_post_processor.py index 21b51b8e8..b18eb170f 100644 --- a/src/ansible_navigator/configuration_subsystem/navigator_post_processor.py +++ b/src/ansible_navigator/configuration_subsystem/navigator_post_processor.py @@ -323,7 +323,12 @@ def container_options(entry: Entry, config: ApplicationConfiguration) -> PostPro @_post_processor def exec_shell(self, entry: Entry, config: ApplicationConfiguration) -> PostProcessorReturn: # pylint: disable=unused-argument - """Post process help_config""" + """Post process exec_shell. + + :param entry: The current settings entry + :param config: The full application configuration + :return: An instance of the standard post process return object + """ messages, exit_messages = self._true_or_false(entry, config) return messages, exit_messages diff --git a/src/ansible_navigator/utils.py b/src/ansible_navigator/utils.py index 4d655e10b..ce9f8d319 100644 --- a/src/ansible_navigator/utils.py +++ b/src/ansible_navigator/utils.py @@ -349,11 +349,7 @@ def divmod_int(numerator: Union[int, float], denominator: Union[int, float]) -> return int(quotient), int(remainder) -<<<<<<< HEAD def human_time(seconds: Union[int, float]) -> str: -======= -def human_time(seconds: int) -> str: ->>>>>>> 7eb4ca9 (Update docstrings) """Convert seconds into human readable 00d00h00m00s format.""" sign_string = "-" if seconds < 0 else "" seconds = abs(int(seconds)) diff --git a/tests/integration/actions/exec/test_stdout_config_cli.py b/tests/integration/actions/exec/test_stdout_config_cli.py index 9be5de1b8..4268607e5 100644 --- a/tests/integration/actions/exec/test_stdout_config_cli.py +++ b/tests/integration/actions/exec/test_stdout_config_cli.py @@ -66,7 +66,7 @@ class ShellCommand(Step): def step_id(value: ShellCommand) -> str: """Return the test id from the test step object. - :param value: The data fro the test iteration + :param value: The data from the test iteration :returns: An id for the test """ return f"{value.comment} {value.user_input}" diff --git a/tests/integration/actions/exec/test_stdout_config_file.py b/tests/integration/actions/exec/test_stdout_config_file.py index 2f7ebb6c5..e8a9124af 100644 --- a/tests/integration/actions/exec/test_stdout_config_file.py +++ b/tests/integration/actions/exec/test_stdout_config_file.py @@ -51,7 +51,7 @@ class ShellCommand(Step): def step_id(value: ShellCommand) -> str: """Return the test id from the test step object. - :param value: The data fro the test iteration + :param value: The data from the test iteration :returns: An id for the test """ return f"{value.comment} {value.user_input}" From ac20f0f357faa07ad8348216059196e103e1bdcd Mon Sep 17 00:00:00 2001 From: cidrblock Date: Wed, 8 Dec 2021 15:57:10 -0800 Subject: [PATCH 15/59] cfg file is str or None --- tests/integration/actions/exec/base.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/integration/actions/exec/base.py b/tests/integration/actions/exec/base.py index b6a45b49a..b1ed91328 100644 --- a/tests/integration/actions/exec/base.py +++ b/tests/integration/actions/exec/base.py @@ -5,6 +5,7 @@ import os from typing import Generator +from typing import Union import pytest @@ -25,7 +26,7 @@ class BaseClass: UPDATE_FIXTURES = False PANE_HEIGHT = 25 PANE_WIDTH = 300 - CONFIG_FILE = None + CONFIG_FILE: Union[str, None] = None @pytest.fixture(scope="module", name="tmux_session") def fixture_tmux_session( From cc0534df811a6fee4d4215616f30b0eed829d30d Mon Sep 17 00:00:00 2001 From: "Bradley A. Thornton" Date: Fri, 10 Dec 2021 04:03:48 -0800 Subject: [PATCH 16/59] Update src/ansible_navigator/actions/_actions.py Co-authored-by: Sviatoslav Sydorenko --- src/ansible_navigator/actions/_actions.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/ansible_navigator/actions/_actions.py b/src/ansible_navigator/actions/_actions.py index bfd93ccfa..26660b201 100644 --- a/src/ansible_navigator/actions/_actions.py +++ b/src/ansible_navigator/actions/_actions.py @@ -98,10 +98,12 @@ def run_interactive(package: str, action: str, *args: Any, **_kwargs: Any) -> An """Call the given action's run""" action_cls = get(package, action) app, interaction = args - if hasattr(action_cls(app.args), "run"): - return action_cls(app.args).run(app=app, interaction=interaction) - logger.error("Subcommand '%s' does not support mode interactive", action) - return action_cls(app.args).no_interactive_mode(app=app, interaction=interaction) + app_action = action_cls(app.args) + supports_interactive = hasattr(app_action, "run") + if not supports_interactive: + logger.error("Subcommand '%s' does not support mode interactive", action) + run_action = app_action.run if supports_interactive else app_action.no_interactive_mode + return run_action(app=app, interaction=interaction) def run_interactive_factory(package: str) -> Callable: From 179711da929b8f1bed0d2f9b0ea384d834bafe9a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 10 Dec 2021 12:04:06 +0000 Subject: [PATCH 17/59] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/fixtures/integration/actions/exec/ansible-navigator.yaml | 2 +- .../actions/exec/test_stdout_config_cli.py/test/0.json | 2 +- .../actions/exec/test_stdout_config_cli.py/test/1.json | 2 +- .../actions/exec/test_stdout_config_cli.py/test/2.json | 2 +- .../actions/exec/test_stdout_config_cli.py/test/3.json | 2 +- .../actions/exec/test_stdout_config_file.py/test/0.json | 2 +- .../actions/exec/test_stdout_config_file.py/test/1.json | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/fixtures/integration/actions/exec/ansible-navigator.yaml b/tests/fixtures/integration/actions/exec/ansible-navigator.yaml index 9306f26a4..62742db64 100644 --- a/tests/fixtures/integration/actions/exec/ansible-navigator.yaml +++ b/tests/fixtures/integration/actions/exec/ansible-navigator.yaml @@ -1,4 +1,4 @@ ansible-navigator: exec: command: echo test_data_from_config - shell: False \ No newline at end of file + shell: False diff --git a/tests/fixtures/integration/actions/exec/test_stdout_config_cli.py/test/0.json b/tests/fixtures/integration/actions/exec/test_stdout_config_cli.py/test/0.json index 88feda76f..a29e70993 100644 --- a/tests/fixtures/integration/actions/exec/test_stdout_config_cli.py/test/0.json +++ b/tests/fixtures/integration/actions/exec/test_stdout_config_cli.py/test/0.json @@ -16,4 +16,4 @@ "test_data_from_cli", "(venv) bash-5.1$" ] -} \ No newline at end of file +} diff --git a/tests/fixtures/integration/actions/exec/test_stdout_config_cli.py/test/1.json b/tests/fixtures/integration/actions/exec/test_stdout_config_cli.py/test/1.json index b0ea54819..1e3b11215 100644 --- a/tests/fixtures/integration/actions/exec/test_stdout_config_cli.py/test/1.json +++ b/tests/fixtures/integration/actions/exec/test_stdout_config_cli.py/test/1.json @@ -20,4 +20,4 @@ " [HINT]: Review the hints and log file to see what went wrong: /home/user/github/ansible-navigator/ansible-navigator.log", "(venv) bash-5.1$" ] -} \ No newline at end of file +} diff --git a/tests/fixtures/integration/actions/exec/test_stdout_config_cli.py/test/2.json b/tests/fixtures/integration/actions/exec/test_stdout_config_cli.py/test/2.json index 19065707a..6d5b3ec8b 100644 --- a/tests/fixtures/integration/actions/exec/test_stdout_config_cli.py/test/2.json +++ b/tests/fixtures/integration/actions/exec/test_stdout_config_cli.py/test/2.json @@ -13,4 +13,4 @@ "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", "(venv) bash-5.1$" ] -} \ No newline at end of file +} diff --git a/tests/fixtures/integration/actions/exec/test_stdout_config_cli.py/test/3.json b/tests/fixtures/integration/actions/exec/test_stdout_config_cli.py/test/3.json index ce64ce6ac..5e18ccbe4 100644 --- a/tests/fixtures/integration/actions/exec/test_stdout_config_cli.py/test/3.json +++ b/tests/fixtures/integration/actions/exec/test_stdout_config_cli.py/test/3.json @@ -13,4 +13,4 @@ "$PATH", "(venv) bash-5.1$" ] -} \ No newline at end of file +} diff --git a/tests/fixtures/integration/actions/exec/test_stdout_config_file.py/test/0.json b/tests/fixtures/integration/actions/exec/test_stdout_config_file.py/test/0.json index 5595de128..9aa22a49b 100644 --- a/tests/fixtures/integration/actions/exec/test_stdout_config_file.py/test/0.json +++ b/tests/fixtures/integration/actions/exec/test_stdout_config_file.py/test/0.json @@ -16,4 +16,4 @@ "test_data_from_config", "(venv) bash-5.1$" ] -} \ No newline at end of file +} diff --git a/tests/fixtures/integration/actions/exec/test_stdout_config_file.py/test/1.json b/tests/fixtures/integration/actions/exec/test_stdout_config_file.py/test/1.json index b4c374ab9..71a459175 100644 --- a/tests/fixtures/integration/actions/exec/test_stdout_config_file.py/test/1.json +++ b/tests/fixtures/integration/actions/exec/test_stdout_config_file.py/test/1.json @@ -13,4 +13,4 @@ "$PATH", "(venv) bash-5.1$" ] -} \ No newline at end of file +} From 285be5747b58bb2b8220ed698c7aa92ba164ab30 Mon Sep 17 00:00:00 2001 From: "Bradley A. Thornton" Date: Fri, 10 Dec 2021 04:09:22 -0800 Subject: [PATCH 18/59] Update src/ansible_navigator/actions/exec.py Co-authored-by: Sviatoslav Sydorenko --- src/ansible_navigator/actions/exec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ansible_navigator/actions/exec.py b/src/ansible_navigator/actions/exec.py index 7f18bbdc9..be8b9e34a 100644 --- a/src/ansible_navigator/actions/exec.py +++ b/src/ansible_navigator/actions/exec.py @@ -67,7 +67,7 @@ def _run_runner(self) -> Optional[Tuple]: :return: The stdout, stderr and return code from runner """ if isinstance(self._args.set_environment_variable, dict): - set_envvars = {**self._args.set_environment_variable} + set_envvars = self._args.set_environment_variable.copy() else: set_envvars = {} From 9e94c6698a3031e89d1334c99fd09fab99fbaa7c Mon Sep 17 00:00:00 2001 From: cidrblock Date: Fri, 10 Dec 2021 04:19:21 -0800 Subject: [PATCH 19/59] rename var --- src/ansible_navigator/actions/exec.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/ansible_navigator/actions/exec.py b/src/ansible_navigator/actions/exec.py index be8b9e34a..05ad7664c 100644 --- a/src/ansible_navigator/actions/exec.py +++ b/src/ansible_navigator/actions/exec.py @@ -67,12 +67,12 @@ def _run_runner(self) -> Optional[Tuple]: :return: The stdout, stderr and return code from runner """ if isinstance(self._args.set_environment_variable, dict): - set_envvars = self._args.set_environment_variable.copy() + envvars_to_set = self._args.set_environment_variable.copy() else: - set_envvars = {} + envvars_to_set = {} if self._args.display_color is False: - set_envvars["ANSIBLE_NOCOLOR"] = "1" + envvars_to_set["ANSIBLE_NOCOLOR"] = "1" kwargs = { "container_engine": self._args.container_engine, @@ -81,7 +81,7 @@ def _run_runner(self) -> Optional[Tuple]: "execution_environment": self._args.execution_environment, "navigator_mode": self._args.mode, "pass_environment_variable": self._args.pass_environment_variable, - "set_environment_variable": set_envvars, + "set_environment_variable": envvars_to_set, "timeout": self._args.ansible_runner_timeout, } From a60199dd4cf070d1cdf44ec964cb82e6eecd240c Mon Sep 17 00:00:00 2001 From: cidrblock Date: Fri, 10 Dec 2021 04:21:58 -0800 Subject: [PATCH 20/59] Update changelog --- docs/changelog-fragments.d/526.feature.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/changelog-fragments.d/526.feature.md b/docs/changelog-fragments.d/526.feature.md index 5ec131ae1..3d87e7c5a 100644 --- a/docs/changelog-fragments.d/526.feature.md +++ b/docs/changelog-fragments.d/526.feature.md @@ -1,2 +1,3 @@ -Add ability to run commands directly within an execution environment. +Added the ability to run ad-hoc commands directly within an execution environment +using mode `stdout`. -- by {user}`cidrblock` From 94a7199e7449a3a90606018410cc9b54a04cc048 Mon Sep 17 00:00:00 2001 From: cidrblock Date: Fri, 10 Dec 2021 04:37:51 -0800 Subject: [PATCH 21/59] err for env type, update changelog --- docs/changelog-fragments.d/526.feature.md | 5 ++--- src/ansible_navigator/actions/exec.py | 6 ++++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/changelog-fragments.d/526.feature.md b/docs/changelog-fragments.d/526.feature.md index 3d87e7c5a..fdf7b5f01 100644 --- a/docs/changelog-fragments.d/526.feature.md +++ b/docs/changelog-fragments.d/526.feature.md @@ -1,3 +1,2 @@ -Added the ability to run ad-hoc commands directly within an execution environment -using mode `stdout`. --- by {user}`cidrblock` +Added the ability to run ad-hoc commands directly within an execution +environment using mode `stdout`. -- by {user}`cidrblock` diff --git a/src/ansible_navigator/actions/exec.py b/src/ansible_navigator/actions/exec.py index 05ad7664c..16bb86b96 100644 --- a/src/ansible_navigator/actions/exec.py +++ b/src/ansible_navigator/actions/exec.py @@ -10,6 +10,8 @@ from . import _actions as actions from ..app import App from ..configuration_subsystem import ApplicationConfiguration +from ..configuration_subsystem.definitions import Constants + from ..runner import Command @@ -68,7 +70,11 @@ def _run_runner(self) -> Optional[Tuple]: """ if isinstance(self._args.set_environment_variable, dict): envvars_to_set = self._args.set_environment_variable.copy() + elif isinstance(self._args.set_environment_variable, Constants): + envvars_to_set = {} else: + self._logger.error("The setting 'set_environment_variable' was neither a dictionary" + " or Constants, please raise an issue. No environment variables will be set.") envvars_to_set = {} if self._args.display_color is False: From 2de192ad57e179e32bcab6833dc422ec13000089 Mon Sep 17 00:00:00 2001 From: "Bradley A. Thornton" Date: Fri, 10 Dec 2021 04:38:23 -0800 Subject: [PATCH 22/59] Update src/ansible_navigator/app.py Co-authored-by: Sviatoslav Sydorenko --- src/ansible_navigator/app.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/ansible_navigator/app.py b/src/ansible_navigator/app.py index 1420e6b22..a03de4d8d 100644 --- a/src/ansible_navigator/app.py +++ b/src/ansible_navigator/app.py @@ -22,7 +22,6 @@ from .ui_framework import ui from .ui_framework import warning_notification - from .utils import LogMessage from .utils import ExitMessage From 5b634d1034e076f4d2021836504dc4aa12d68841 Mon Sep 17 00:00:00 2001 From: "Bradley A. Thornton" Date: Fri, 10 Dec 2021 04:38:37 -0800 Subject: [PATCH 23/59] Update src/ansible_navigator/app.py Co-authored-by: Sviatoslav Sydorenko --- src/ansible_navigator/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ansible_navigator/app.py b/src/ansible_navigator/app.py index a03de4d8d..6f6caf492 100644 --- a/src/ansible_navigator/app.py +++ b/src/ansible_navigator/app.py @@ -86,7 +86,7 @@ def app(self) -> AppPublic: def no_interactive_mode(self, interaction: Interaction, app: AppPublic) -> None: # pylint: disable=unused-argument - """Warn the user interactive mode is not supported""" + """Show a warning notification that the user interactive mode is not supported.""" warning = warning_notification( messages=[ f"The '{self._name}' subcommand is not available while using interactive mode.", From c90d76a0d4703e45cd2f0d82ddf10fe18d37ee06 Mon Sep 17 00:00:00 2001 From: "Bradley A. Thornton" Date: Fri, 10 Dec 2021 04:40:15 -0800 Subject: [PATCH 24/59] Update src/ansible_navigator/configuration_subsystem/navigator_post_processor.py Co-authored-by: Sviatoslav Sydorenko --- .../configuration_subsystem/navigator_post_processor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ansible_navigator/configuration_subsystem/navigator_post_processor.py b/src/ansible_navigator/configuration_subsystem/navigator_post_processor.py index b18eb170f..afc53e17b 100644 --- a/src/ansible_navigator/configuration_subsystem/navigator_post_processor.py +++ b/src/ansible_navigator/configuration_subsystem/navigator_post_processor.py @@ -323,7 +323,7 @@ def container_options(entry: Entry, config: ApplicationConfiguration) -> PostPro @_post_processor def exec_shell(self, entry: Entry, config: ApplicationConfiguration) -> PostProcessorReturn: # pylint: disable=unused-argument - """Post process exec_shell. + """Post process ``exec_shell``. :param entry: The current settings entry :param config: The full application configuration From a1a9664aa33e14c9994c2c747165c3159ccb0dfa Mon Sep 17 00:00:00 2001 From: "Bradley A. Thornton" Date: Fri, 10 Dec 2021 04:40:26 -0800 Subject: [PATCH 25/59] Update tests/integration/actions/exec/base.py Co-authored-by: Sviatoslav Sydorenko --- tests/integration/actions/exec/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/actions/exec/base.py b/tests/integration/actions/exec/base.py index b1ed91328..d2df47eae 100644 --- a/tests/integration/actions/exec/base.py +++ b/tests/integration/actions/exec/base.py @@ -50,7 +50,7 @@ def fixture_tmux_session( yield tmux_session def test(self, request: pytest.FixtureRequest, tmux_session: TmuxSession, step: Step): - # pylint:disable=unused-argument + # pylint: disable=unused-argument # pylint: disable=too-few-public-methods # pylint: disable=too-many-arguments """Test interactive/stdout exec. From f019d831116cd98a9d37a3121e8a7f71c9994f6a Mon Sep 17 00:00:00 2001 From: "Bradley A. Thornton" Date: Fri, 10 Dec 2021 04:40:45 -0800 Subject: [PATCH 26/59] Update tests/integration/actions/exec/__init__.py Co-authored-by: Sviatoslav Sydorenko --- tests/integration/actions/exec/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/actions/exec/__init__.py b/tests/integration/actions/exec/__init__.py index e90e5f8c3..93c18b31b 100644 --- a/tests/integration/actions/exec/__init__.py +++ b/tests/integration/actions/exec/__init__.py @@ -1 +1 @@ -"""Integration tests for the exec subcommand.""" +"""Integration tests for the ``exec`` subcommand.""" From 3c6832de5f850756ee19d9bc347ca541d1d1c426 Mon Sep 17 00:00:00 2001 From: "Bradley A. Thornton" Date: Fri, 10 Dec 2021 04:43:54 -0800 Subject: [PATCH 27/59] Update tests/integration/actions/exec/base.py Co-authored-by: Sviatoslav Sydorenko --- tests/integration/actions/exec/base.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/integration/actions/exec/base.py b/tests/integration/actions/exec/base.py index d2df47eae..3d805fb09 100644 --- a/tests/integration/actions/exec/base.py +++ b/tests/integration/actions/exec/base.py @@ -70,10 +70,11 @@ def test(self, request: pytest.FixtureRequest, tmux_session: TmuxSession, step: search_within_response=search_within_response, ) - if ( + fixtures_update_requested = ( self.UPDATE_FIXTURES or os.environ.get("ANSIBLE_NAVIGATOR_UPDATE_TEST_FIXTURES") == "true" - ): + ) + if fixtures_update_requested: update_fixtures( request, step.step_index, From 7a72e58c55e01d80b21fd03cfc1a02380ea83e21 Mon Sep 17 00:00:00 2001 From: cidrblock Date: Fri, 10 Dec 2021 05:16:33 -0800 Subject: [PATCH 28/59] switch to pathlib --- tests/integration/actions/exec/base.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/integration/actions/exec/base.py b/tests/integration/actions/exec/base.py index 3d805fb09..f286eca84 100644 --- a/tests/integration/actions/exec/base.py +++ b/tests/integration/actions/exec/base.py @@ -4,6 +4,8 @@ import json import os +from pathlib import Path +from pathlib import PosixPath from typing import Generator from typing import Union @@ -16,8 +18,8 @@ from ..._interactions import Step from ....defaults import FIXTURES_DIR -TEST_FIXTURE_DIR = os.path.join(FIXTURES_DIR, "integration", "actions", "exec") -TEST_CONFIG_FILE = os.path.join(TEST_FIXTURE_DIR, "ansible-navigator.yaml") +TEST_FIXTURE_DIR = Path(FIXTURES_DIR, "integration", "actions", "exec") +TEST_CONFIG_FILE = Path(TEST_FIXTURE_DIR, "ansible-navigator.yaml") class BaseClass: @@ -26,7 +28,7 @@ class BaseClass: UPDATE_FIXTURES = False PANE_HEIGHT = 25 PANE_WIDTH = 300 - CONFIG_FILE: Union[str, None] = None + CONFIG_FILE: Union[PosixPath, None] = None @pytest.fixture(scope="module", name="tmux_session") def fixture_tmux_session( @@ -42,8 +44,8 @@ def fixture_tmux_session( "pane_height": self.PANE_HEIGHT, "pane_width": self.PANE_WIDTH, } - if isinstance(self.CONFIG_FILE, str): - assert os.path.exists(self.CONFIG_FILE) + if isinstance(self.CONFIG_FILE, PosixPath): + assert self.CONFIG_FILE.exists() params["config_path"] = self.CONFIG_FILE with TmuxSession(**params) as tmux_session: From 16b6735ec7edbe03dca3317b6b8ae8e5658a6811 Mon Sep 17 00:00:00 2001 From: cidrblock Date: Fri, 10 Dec 2021 05:25:40 -0800 Subject: [PATCH 29/59] add current --- src/ansible_navigator/actions/exec.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ansible_navigator/actions/exec.py b/src/ansible_navigator/actions/exec.py index 16bb86b96..dd45ca53d 100644 --- a/src/ansible_navigator/actions/exec.py +++ b/src/ansible_navigator/actions/exec.py @@ -74,7 +74,8 @@ def _run_runner(self) -> Optional[Tuple]: envvars_to_set = {} else: self._logger.error("The setting 'set_environment_variable' was neither a dictionary" - " or Constants, please raise an issue. No environment variables will be set.") + " or Constants, please raise an issue. No environment variables will be set." + " The current value was found to be '%s'", self._args.set_environment_variable) envvars_to_set = {} if self._args.display_color is False: From 3cfed8a31f3cd2be720e8e81680f915726c7899a Mon Sep 17 00:00:00 2001 From: cidrblock Date: Fri, 10 Dec 2021 05:26:18 -0800 Subject: [PATCH 30/59] long line --- src/ansible_navigator/actions/exec.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ansible_navigator/actions/exec.py b/src/ansible_navigator/actions/exec.py index dd45ca53d..f90024cee 100644 --- a/src/ansible_navigator/actions/exec.py +++ b/src/ansible_navigator/actions/exec.py @@ -73,9 +73,12 @@ def _run_runner(self) -> Optional[Tuple]: elif isinstance(self._args.set_environment_variable, Constants): envvars_to_set = {} else: - self._logger.error("The setting 'set_environment_variable' was neither a dictionary" + self._logger.error( + "The setting 'set_environment_variable' was neither a dictionary" " or Constants, please raise an issue. No environment variables will be set." - " The current value was found to be '%s'", self._args.set_environment_variable) + " The current value was found to be '%s'", + self._args.set_environment_variable, + ) envvars_to_set = {} if self._args.display_color is False: From 12e40846448a1dbdd6b8c9f58ee1342f98de0819 Mon Sep 17 00:00:00 2001 From: cidrblock Date: Fri, 10 Dec 2021 05:29:41 -0800 Subject: [PATCH 31/59] type fix --- tests/integration/actions/exec/base.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/integration/actions/exec/base.py b/tests/integration/actions/exec/base.py index f286eca84..293f300f6 100644 --- a/tests/integration/actions/exec/base.py +++ b/tests/integration/actions/exec/base.py @@ -5,7 +5,6 @@ import os from pathlib import Path -from pathlib import PosixPath from typing import Generator from typing import Union @@ -28,7 +27,7 @@ class BaseClass: UPDATE_FIXTURES = False PANE_HEIGHT = 25 PANE_WIDTH = 300 - CONFIG_FILE: Union[PosixPath, None] = None + CONFIG_FILE: Union[Path, None] = None @pytest.fixture(scope="module", name="tmux_session") def fixture_tmux_session( @@ -44,7 +43,7 @@ def fixture_tmux_session( "pane_height": self.PANE_HEIGHT, "pane_width": self.PANE_WIDTH, } - if isinstance(self.CONFIG_FILE, PosixPath): + if isinstance(self.CONFIG_FILE, Path): assert self.CONFIG_FILE.exists() params["config_path"] = self.CONFIG_FILE From e0e7e98ee80db8844f8ad0f6d78bb1995b962f05 Mon Sep 17 00:00:00 2001 From: cidrblock Date: Fri, 10 Dec 2021 05:36:24 -0800 Subject: [PATCH 32/59] see what sample looks like in changelog --- docs/changelog-fragments.d/526.feature.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/changelog-fragments.d/526.feature.md b/docs/changelog-fragments.d/526.feature.md index fdf7b5f01..8b650b5af 100644 --- a/docs/changelog-fragments.d/526.feature.md +++ b/docs/changelog-fragments.d/526.feature.md @@ -1,2 +1,8 @@ Added the ability to run ad-hoc commands directly within an execution -environment using mode `stdout`. -- by {user}`cidrblock` +environment using mode `stdout`. + +``` +ansible-navigator exec --mode stdout +``` + +-- by {user}`cidrblock` From b57d9b2cb59f59f5cefac71614e2cfa6f015fdfd Mon Sep 17 00:00:00 2001 From: cidrblock Date: Fri, 10 Dec 2021 05:42:30 -0800 Subject: [PATCH 33/59] shell and $ test --- docs/changelog-fragments.d/526.feature.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changelog-fragments.d/526.feature.md b/docs/changelog-fragments.d/526.feature.md index 8b650b5af..1ac82c962 100644 --- a/docs/changelog-fragments.d/526.feature.md +++ b/docs/changelog-fragments.d/526.feature.md @@ -1,8 +1,8 @@ Added the ability to run ad-hoc commands directly within an execution environment using mode `stdout`. -``` -ansible-navigator exec --mode stdout +```{shell} +$ ansible-navigator exec --mode stdout ``` -- by {user}`cidrblock` From 04fec4796045cf4956f6a072e2a1560a55da1af7 Mon Sep 17 00:00:00 2001 From: cidrblock Date: Fri, 10 Dec 2021 05:54:14 -0800 Subject: [PATCH 34/59] console --- docs/changelog-fragments.d/526.feature.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog-fragments.d/526.feature.md b/docs/changelog-fragments.d/526.feature.md index 1ac82c962..0e2e48226 100644 --- a/docs/changelog-fragments.d/526.feature.md +++ b/docs/changelog-fragments.d/526.feature.md @@ -1,7 +1,7 @@ Added the ability to run ad-hoc commands directly within an execution environment using mode `stdout`. -```{shell} +```{console} $ ansible-navigator exec --mode stdout ``` From 02408de4c52f28ff436efd133ff59a602afb633f Mon Sep 17 00:00:00 2001 From: cidrblock Date: Fri, 10 Dec 2021 05:59:56 -0800 Subject: [PATCH 35/59] bash --- docs/changelog-fragments.d/526.feature.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog-fragments.d/526.feature.md b/docs/changelog-fragments.d/526.feature.md index 0e2e48226..44fa08683 100644 --- a/docs/changelog-fragments.d/526.feature.md +++ b/docs/changelog-fragments.d/526.feature.md @@ -1,7 +1,7 @@ Added the ability to run ad-hoc commands directly within an execution environment using mode `stdout`. -```{console} +```{bash} $ ansible-navigator exec --mode stdout ``` From 4a97a0b66472d6dc5df1336e6ed49d2f48f50031 Mon Sep 17 00:00:00 2001 From: cidrblock Date: Fri, 10 Dec 2021 06:17:27 -0800 Subject: [PATCH 36/59] rm directive --- docs/changelog-fragments.d/526.feature.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog-fragments.d/526.feature.md b/docs/changelog-fragments.d/526.feature.md index 44fa08683..2f8e10bae 100644 --- a/docs/changelog-fragments.d/526.feature.md +++ b/docs/changelog-fragments.d/526.feature.md @@ -1,7 +1,7 @@ Added the ability to run ad-hoc commands directly within an execution environment using mode `stdout`. -```{bash} +``` $ ansible-navigator exec --mode stdout ``` From 000484e0af34f256d32fd269377ebefb2478fb50 Mon Sep 17 00:00:00 2001 From: cidrblock Date: Fri, 10 Dec 2021 13:33:40 -0800 Subject: [PATCH 37/59] Changes recommended by various linters --- src/ansible_navigator/actions/_actions.py | 9 ++-- src/ansible_navigator/actions/exec.py | 47 ++++++++++--------- .../navigator_post_processor.py | 3 +- tests/integration/actions/exec/base.py | 27 ++++++----- .../actions/exec/test_stdout_config_cli.py | 2 +- .../actions/exec/test_stdout_config_file.py | 4 +- tests/unit/actions/test_exec.py | 26 +++++----- .../test_precedence.py | 6 ++- 8 files changed, 64 insertions(+), 60 deletions(-) diff --git a/src/ansible_navigator/actions/_actions.py b/src/ansible_navigator/actions/_actions.py index 26660b201..9ad3c42d3 100644 --- a/src/ansible_navigator/actions/_actions.py +++ b/src/ansible_navigator/actions/_actions.py @@ -98,12 +98,11 @@ def run_interactive(package: str, action: str, *args: Any, **_kwargs: Any) -> An """Call the given action's run""" action_cls = get(package, action) app, interaction = args - app_action = action_cls(app.args) - supports_interactive = hasattr(app_action, "run") - if not supports_interactive: + try: + return action_cls(app.args).run(app=app, interaction=interaction) + except AttributeError: logger.error("Subcommand '%s' does not support mode interactive", action) - run_action = app_action.run if supports_interactive else app_action.no_interactive_mode - return run_action(app=app, interaction=interaction) + return action_cls(app.args).no_interactive_mode(app=app, interaction=interaction) def run_interactive_factory(package: str) -> Callable: diff --git a/src/ansible_navigator/actions/exec.py b/src/ansible_navigator/actions/exec.py index f90024cee..c0531b30f 100644 --- a/src/ansible_navigator/actions/exec.py +++ b/src/ansible_navigator/actions/exec.py @@ -14,6 +14,27 @@ from ..runner import Command +GeneratedCommand = Tuple[str, Optional[List[str]]] + + +def _generate_command(exec_command: str, exec_shell: bool) -> GeneratedCommand: + """Generate the command and args. + + :param exec_command: The command to run + :param exec_shell: Should the command be wrapped in a shell + :returns: The command and any pass through arguments + """ + pass_through_args = None + if exec_shell and exec_command: + command = "/bin/bash" + pass_through_args = ["-c", exec_command] + else: + parts = shlex.split(exec_command) + command = parts[0] + if len(parts) > 1: + pass_through_args = parts[1:] + return (command, pass_through_args) + @actions.register class Action(App): @@ -21,7 +42,7 @@ class Action(App): # pylint: disable=too-few-public-methods - KEGEX = r"^e(?:xec)?$" + KEGEX = "^e(?:xec)?$" def __init__(self, args: ApplicationConfiguration): """Initialize the action. @@ -30,25 +51,6 @@ def __init__(self, args: ApplicationConfiguration): """ super().__init__(args=args, logger_name=__name__, name="exec") - @staticmethod - def _generate_command(exec_command: str, exec_shell: bool) -> Tuple[str, Optional[List[str]]]: - """Generate the command and args. - - :param exec_command: The command to run - :param exec_shell: Should the command be wrapped in a shell - :returns: The command and any pass through arguments - """ - pass_through_args = None - if exec_shell and exec_command: - command = "/bin/bash" - pass_through_args = ["-c", exec_command] - else: - parts = shlex.split(exec_command) - command = parts[0] - if len(parts) > 1: - pass_through_args = parts[1:] - return (command, pass_through_args) - def run_stdout(self) -> Union[None, int]: """Run in mode stdout. @@ -101,12 +103,11 @@ def _run_runner(self) -> Optional[Tuple]: if isinstance(self._args.container_options, list): kwargs["container_options"] = self._args.container_options - command, pass_through_args = self._generate_command( + command, pass_through_args = _generate_command( exec_command=self._args.exec_command, exec_shell=self._args.exec_shell ) if isinstance(pass_through_args, list): kwargs["cmdline"] = pass_through_args runner = Command(executable_cmd=command, **kwargs) - runner_return = runner.run() - return runner_return + return runner.run() diff --git a/src/ansible_navigator/configuration_subsystem/navigator_post_processor.py b/src/ansible_navigator/configuration_subsystem/navigator_post_processor.py index afc53e17b..77ce4a2ee 100644 --- a/src/ansible_navigator/configuration_subsystem/navigator_post_processor.py +++ b/src/ansible_navigator/configuration_subsystem/navigator_post_processor.py @@ -329,8 +329,7 @@ def exec_shell(self, entry: Entry, config: ApplicationConfiguration) -> PostProc :param config: The full application configuration :return: An instance of the standard post process return object """ - messages, exit_messages = self._true_or_false(entry, config) - return messages, exit_messages + return self._true_or_false(entry, config) @_post_processor def help_config(self, entry: Entry, config: ApplicationConfiguration) -> PostProcessorReturn: diff --git a/tests/integration/actions/exec/base.py b/tests/integration/actions/exec/base.py index 293f300f6..757325cc8 100644 --- a/tests/integration/actions/exec/base.py +++ b/tests/integration/actions/exec/base.py @@ -24,10 +24,10 @@ class BaseClass: """The base class for interactive/stdout exec tests.""" - UPDATE_FIXTURES = False - PANE_HEIGHT = 25 - PANE_WIDTH = 300 - CONFIG_FILE: Union[Path, None] = None + update_fixtures = False + pane_height = 25 + pane_width = 300 + config_file: Union[Path, None] = None @pytest.fixture(scope="module", name="tmux_session") def fixture_tmux_session( @@ -38,16 +38,16 @@ def fixture_tmux_session( :param request: The request for this fixture :yields: A tmux session """ - params = { + tmux_params = { "unique_test_id": request.node.nodeid, - "pane_height": self.PANE_HEIGHT, - "pane_width": self.PANE_WIDTH, + "pane_height": self.pane_height, + "pane_width": self.pane_width, } - if isinstance(self.CONFIG_FILE, Path): - assert self.CONFIG_FILE.exists() - params["config_path"] = self.CONFIG_FILE + if isinstance(self.config_file, Path): + assert self.config_file.exists() + tmux_params["config_path"] = self.config_file - with TmuxSession(**params) as tmux_session: + with TmuxSession(**tmux_params) as tmux_session: yield tmux_session def test(self, request: pytest.FixtureRequest, tmux_session: TmuxSession, step: Step): @@ -72,7 +72,7 @@ def test(self, request: pytest.FixtureRequest, tmux_session: TmuxSession, step: ) fixtures_update_requested = ( - self.UPDATE_FIXTURES + self.update_fixtures or os.environ.get("ANSIBLE_NAVIGATOR_UPDATE_TEST_FIXTURES") == "true" ) if fixtures_update_requested: @@ -101,6 +101,7 @@ def test(self, request: pytest.FixtureRequest, tmux_session: TmuxSession, step: with open(f"{dir_path}/{file_name}") as infile: expected_output = json.load(infile)["output"] - assert expected_output == received_output, "\n" + "\n".join( + diff = "\n".join( difflib.unified_diff(expected_output, received_output, "expected", "received") ) + assert expected_output == received_output, f"\n{diff}" diff --git a/tests/integration/actions/exec/test_stdout_config_cli.py b/tests/integration/actions/exec/test_stdout_config_cli.py index 4268607e5..0b84173ec 100644 --- a/tests/integration/actions/exec/test_stdout_config_cli.py +++ b/tests/integration/actions/exec/test_stdout_config_cli.py @@ -76,4 +76,4 @@ def step_id(value: ShellCommand) -> str: class Test(BaseClass): """Run the tests for exec, mode stdout, parameters set using cli.""" - UPDATE_FIXTURES = False + update_fixtures = False diff --git a/tests/integration/actions/exec/test_stdout_config_file.py b/tests/integration/actions/exec/test_stdout_config_file.py index e8a9124af..f06c87293 100644 --- a/tests/integration/actions/exec/test_stdout_config_file.py +++ b/tests/integration/actions/exec/test_stdout_config_file.py @@ -61,5 +61,5 @@ def step_id(value: ShellCommand) -> str: class Test(BaseClass): """Run the tests for exec, mode stdout, parameters set using config file.""" - CONFIG_FILE = TEST_CONFIG_FILE - UPDATE_FIXTURES = False + config_file = TEST_CONFIG_FILE + update_fixtures = False diff --git a/tests/unit/actions/test_exec.py b/tests/unit/actions/test_exec.py index 7f5eee945..bb7427f86 100644 --- a/tests/unit/actions/test_exec.py +++ b/tests/unit/actions/test_exec.py @@ -5,7 +5,7 @@ import pytest -from ansible_navigator.actions.exec import Action as ExecAction +from ansible_navigator.actions.exec import _generate_command class CommandTestData(NamedTuple): @@ -15,16 +15,16 @@ class CommandTestData(NamedTuple): command: str shell: bool result_command: str - result_params: list + result_params: List -def id_from_data(value): +def id_from_data(test_value): """Return the name from the test data object. - :param value: The value from which the test id will be extracted + :param test_value: The value from which the test id will be extracted :returns: The test id """ - return f" {value.name} " + return f" {test_value.name} " command_test_data = [ @@ -76,14 +76,16 @@ def id_from_data(value): ] -@pytest.mark.parametrize("data", command_test_data, ids=id_from_data) -def test_artifact_path(data: CommandTestData): +@pytest.mark.parametrize("cmd_test_data", command_test_data, ids=id_from_data) +def test_artifact_path(cmd_test_data: CommandTestData): """Test the generation of the command and params. - :param data: The test data + :param cmd_test_data: The test data """ # pylint: disable=protected-access - command, params = ExecAction._generate_command(exec_command=data.command, exec_shell=data.shell) - comment = data, command, params - assert command == data.result_command, comment - assert params == data.result_params, comment + command, additional_params = _generate_command( + exec_command=cmd_test_data.command, exec_shell=cmd_test_data.shell + ) + comment = command_test_data, command, additional_params + assert command == cmd_test_data.result_command, comment + assert additional_params == cmd_test_data.result_params, comment diff --git a/tests/unit/configuration_subsystem/test_precedence.py b/tests/unit/configuration_subsystem/test_precedence.py index b15f6338b..08a893cf4 100644 --- a/tests/unit/configuration_subsystem/test_precedence.py +++ b/tests/unit/configuration_subsystem/test_precedence.py @@ -49,7 +49,8 @@ def test_all_entries_reflect_cli_given_envvars( params = shlex.split(cli_entry) expected = dict(expected) else: - params = shlex.split(cli_entry) + " ".join(base.splitlines()).split() + cli_entry_split = shlex.split(cli_entry) + params = cli_entry_split + " ".join(base.splitlines()).split() expected = {**dict(expected), **dict(BASE_EXPECTED)} envvars = {} @@ -86,7 +87,8 @@ def test_all_entries_reflect_cli_given_settings( params = shlex.split(cli_entry) expected = dict(expected) else: - params = shlex.split(cli_entry) + " ".join(base.splitlines()).split() + cli_entry_split = shlex.split(cli_entry) + params = cli_entry_split + " ".join(base.splitlines()).split() expected = {**dict(expected), **dict(BASE_EXPECTED)} response = generate_config(params=params, setting_file_name=settings) From b2a4cc0c22bb1feb1e64357711fad70f4974b770 Mon Sep 17 00:00:00 2001 From: cidrblock Date: Sun, 5 Dec 2021 08:10:55 -0800 Subject: [PATCH 38/59] Update docstrings --- src/ansible_navigator/utils.py | 4 ++++ tests/integration/actions/exec/base.py | 3 --- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ansible_navigator/utils.py b/src/ansible_navigator/utils.py index ce9f8d319..4d655e10b 100644 --- a/src/ansible_navigator/utils.py +++ b/src/ansible_navigator/utils.py @@ -349,7 +349,11 @@ def divmod_int(numerator: Union[int, float], denominator: Union[int, float]) -> return int(quotient), int(remainder) +<<<<<<< HEAD def human_time(seconds: Union[int, float]) -> str: +======= +def human_time(seconds: int) -> str: +>>>>>>> 7eb4ca9 (Update docstrings) """Convert seconds into human readable 00d00h00m00s format.""" sign_string = "-" if seconds < 0 else "" seconds = abs(int(seconds)) diff --git a/tests/integration/actions/exec/base.py b/tests/integration/actions/exec/base.py index 757325cc8..36093b255 100644 --- a/tests/integration/actions/exec/base.py +++ b/tests/integration/actions/exec/base.py @@ -51,9 +51,6 @@ def fixture_tmux_session( yield tmux_session def test(self, request: pytest.FixtureRequest, tmux_session: TmuxSession, step: Step): - # pylint: disable=unused-argument - # pylint: disable=too-few-public-methods - # pylint: disable=too-many-arguments """Test interactive/stdout exec. :param request: The test request From 8dbda2ff5384ae2370b3ebd71fd850e46c8db1bb Mon Sep 17 00:00:00 2001 From: cidrblock Date: Sat, 11 Dec 2021 06:05:13 -0800 Subject: [PATCH 39/59] Additional cleanup for linters --- src/ansible_navigator/actions/exec.py | 8 ++++---- src/ansible_navigator/app.py | 2 +- tests/integration/actions/exec/base.py | 10 ++++++++-- .../actions/exec/test_stdout_config_cli.py | 6 +++--- .../actions/exec/test_stdout_config_file.py | 6 +++--- tests/unit/actions/test_exec.py | 17 +++++++---------- 6 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/ansible_navigator/actions/exec.py b/src/ansible_navigator/actions/exec.py index c0531b30f..7e30445d9 100644 --- a/src/ansible_navigator/actions/exec.py +++ b/src/ansible_navigator/actions/exec.py @@ -11,7 +11,6 @@ from ..app import App from ..configuration_subsystem import ApplicationConfiguration from ..configuration_subsystem.definitions import Constants - from ..runner import Command GeneratedCommand = Tuple[str, Optional[List[str]]] @@ -77,8 +76,8 @@ def _run_runner(self) -> Optional[Tuple]: else: self._logger.error( "The setting 'set_environment_variable' was neither a dictionary" - " or Constants, please raise an issue. No environment variables will be set." - " The current value was found to be '%s'", + + " or Constants, please raise an issue. No environment variables will be set." + + " The current value was found to be '%s'", self._args.set_environment_variable, ) envvars_to_set = {} @@ -104,7 +103,8 @@ def _run_runner(self) -> Optional[Tuple]: kwargs["container_options"] = self._args.container_options command, pass_through_args = _generate_command( - exec_command=self._args.exec_command, exec_shell=self._args.exec_shell + exec_command=self._args.exec_command, + exec_shell=self._args.exec_shell, ) if isinstance(pass_through_args, list): kwargs["cmdline"] = pass_through_args diff --git a/src/ansible_navigator/app.py b/src/ansible_navigator/app.py index 6f6caf492..8c23a6d0d 100644 --- a/src/ansible_navigator/app.py +++ b/src/ansible_navigator/app.py @@ -91,7 +91,7 @@ def no_interactive_mode(self, interaction: Interaction, app: AppPublic) -> None: messages=[ f"The '{self._name}' subcommand is not available while using interactive mode.", "[HINT] Start an additional instance of ansible-navigator" - " in a new terminal with mode 'stdout'.", + + " in a new terminal with mode 'stdout'.", f" e.g. 'ansible-navigator {self._name} --mode stdout", ] ) diff --git a/tests/integration/actions/exec/base.py b/tests/integration/actions/exec/base.py index 36093b255..7c9eb19c2 100644 --- a/tests/integration/actions/exec/base.py +++ b/tests/integration/actions/exec/base.py @@ -31,7 +31,8 @@ class BaseClass: @pytest.fixture(scope="module", name="tmux_session") def fixture_tmux_session( - self, request: pytest.FixtureRequest + self, + request: pytest.FixtureRequest, ) -> Generator[TmuxSession, None, None]: """Tmux fixture for this module. @@ -99,6 +100,11 @@ def test(self, request: pytest.FixtureRequest, tmux_session: TmuxSession, step: expected_output = json.load(infile)["output"] diff = "\n".join( - difflib.unified_diff(expected_output, received_output, "expected", "received") + difflib.unified_diff( + expected_output, + received_output, + "expected", + "received", + ) ) assert expected_output == received_output, f"\n{diff}" diff --git a/tests/integration/actions/exec/test_stdout_config_cli.py b/tests/integration/actions/exec/test_stdout_config_cli.py index 0b84173ec..dd61799bb 100644 --- a/tests/integration/actions/exec/test_stdout_config_cli.py +++ b/tests/integration/actions/exec/test_stdout_config_cli.py @@ -63,13 +63,13 @@ class ShellCommand(Step): steps = add_indicies(stdout_tests) -def step_id(value: ShellCommand) -> str: +def step_id(test_value: ShellCommand) -> str: """Return the test id from the test step object. - :param value: The data from the test iteration + :param test_value: The data from the test iteration :returns: An id for the test """ - return f"{value.comment} {value.user_input}" + return f"{test_value.comment} {test_value.user_input}" @pytest.mark.parametrize("step", steps, ids=step_id) diff --git a/tests/integration/actions/exec/test_stdout_config_file.py b/tests/integration/actions/exec/test_stdout_config_file.py index f06c87293..a0b95952e 100644 --- a/tests/integration/actions/exec/test_stdout_config_file.py +++ b/tests/integration/actions/exec/test_stdout_config_file.py @@ -48,13 +48,13 @@ class ShellCommand(Step): steps = add_indicies(stdout_tests) -def step_id(value: ShellCommand) -> str: +def step_id(test_value: ShellCommand) -> str: """Return the test id from the test step object. - :param value: The data from the test iteration + :param test_value: The data from the test iteration :returns: An id for the test """ - return f"{value.comment} {value.user_input}" + return f"{test_value.comment} {test_value.user_input}" @pytest.mark.parametrize("step", steps, ids=step_id) diff --git a/tests/unit/actions/test_exec.py b/tests/unit/actions/test_exec.py index bb7427f86..01a2bb9a0 100644 --- a/tests/unit/actions/test_exec.py +++ b/tests/unit/actions/test_exec.py @@ -44,24 +44,20 @@ def id_from_data(test_value): ), CommandTestData( name="With shell complex", - command=( - "ansible-vault encrypt_string --vault-password-file" - " a_password_file 'foobar' --name 'the_secret'" - ), + command="ansible-vault encrypt_string --vault-password-file" + + " a_password_file 'foobar' --name 'the_secret'", shell=True, result_command="/bin/bash", result_params=[ "-c", "ansible-vault encrypt_string --vault-password-file" - " a_password_file 'foobar' --name 'the_secret'", + + " a_password_file 'foobar' --name 'the_secret'", ], ), CommandTestData( name="Without shell complex", - command=( - "ansible-vault encrypt_string --vault-password-file" - " a_password_file 'foobar' --name 'the secret'" - ), + command="ansible-vault encrypt_string --vault-password-file" + + " a_password_file 'foobar' --name 'the secret'", shell=False, result_command="ansible-vault", result_params=[ @@ -84,7 +80,8 @@ def test_artifact_path(cmd_test_data: CommandTestData): """ # pylint: disable=protected-access command, additional_params = _generate_command( - exec_command=cmd_test_data.command, exec_shell=cmd_test_data.shell + exec_command=cmd_test_data.command, + exec_shell=cmd_test_data.shell, ) comment = command_test_data, command, additional_params assert command == cmd_test_data.result_command, comment From 840453891af203cf9b6bd007bc27428f4a9eb80c Mon Sep 17 00:00:00 2001 From: cidrblock Date: Sat, 11 Dec 2021 06:13:52 -0800 Subject: [PATCH 40/59] rm merge conflict --- src/ansible_navigator/utils.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/ansible_navigator/utils.py b/src/ansible_navigator/utils.py index 4d655e10b..ce9f8d319 100644 --- a/src/ansible_navigator/utils.py +++ b/src/ansible_navigator/utils.py @@ -349,11 +349,7 @@ def divmod_int(numerator: Union[int, float], denominator: Union[int, float]) -> return int(quotient), int(remainder) -<<<<<<< HEAD def human_time(seconds: Union[int, float]) -> str: -======= -def human_time(seconds: int) -> str: ->>>>>>> 7eb4ca9 (Update docstrings) """Convert seconds into human readable 00d00h00m00s format.""" sign_string = "-" if seconds < 0 else "" seconds = abs(int(seconds)) From 7e1d705f573dd532fe619225534560e407974d99 Mon Sep 17 00:00:00 2001 From: cidrblock Date: Sat, 11 Dec 2021 07:38:47 -0800 Subject: [PATCH 41/59] break string --- src/ansible_navigator/actions/exec.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/ansible_navigator/actions/exec.py b/src/ansible_navigator/actions/exec.py index 7e30445d9..045573853 100644 --- a/src/ansible_navigator/actions/exec.py +++ b/src/ansible_navigator/actions/exec.py @@ -74,13 +74,16 @@ def _run_runner(self) -> Optional[Tuple]: elif isinstance(self._args.set_environment_variable, Constants): envvars_to_set = {} else: - self._logger.error( + log_message = ( "The setting 'set_environment_variable' was neither a dictionary" + " or Constants, please raise an issue. No environment variables will be set." - + " The current value was found to be '%s'", + ) + self._logger.error( + "%s The current value was found to be '%s'", + log_message, self._args.set_environment_variable, ) - envvars_to_set = {} + envvars_to_set = {} if self._args.display_color is False: envvars_to_set["ANSIBLE_NOCOLOR"] = "1" From 3cc4a568d8bd895d2538bb9dadec4d037d46ade3 Mon Sep 17 00:00:00 2001 From: cidrblock Date: Sat, 11 Dec 2021 07:41:16 -0800 Subject: [PATCH 42/59] break string --- tests/integration/actions/exec/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/actions/exec/base.py b/tests/integration/actions/exec/base.py index 7c9eb19c2..f9f1b1680 100644 --- a/tests/integration/actions/exec/base.py +++ b/tests/integration/actions/exec/base.py @@ -105,6 +105,6 @@ def test(self, request: pytest.FixtureRequest, tmux_session: TmuxSession, step: received_output, "expected", "received", - ) + ), ) assert expected_output == received_output, f"\n{diff}" From 35786059bb9b2bbffa5f43bbd9fad52af2bdb61f Mon Sep 17 00:00:00 2001 From: cidrblock Date: Sun, 5 Dec 2021 08:10:55 -0800 Subject: [PATCH 43/59] Update docstrings --- src/ansible_navigator/utils.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ansible_navigator/utils.py b/src/ansible_navigator/utils.py index ce9f8d319..4d655e10b 100644 --- a/src/ansible_navigator/utils.py +++ b/src/ansible_navigator/utils.py @@ -349,7 +349,11 @@ def divmod_int(numerator: Union[int, float], denominator: Union[int, float]) -> return int(quotient), int(remainder) +<<<<<<< HEAD def human_time(seconds: Union[int, float]) -> str: +======= +def human_time(seconds: int) -> str: +>>>>>>> 7eb4ca9 (Update docstrings) """Convert seconds into human readable 00d00h00m00s format.""" sign_string = "-" if seconds < 0 else "" seconds = abs(int(seconds)) From cf4e061d8a3806b4f190e3d446ed22cf8474b86e Mon Sep 17 00:00:00 2001 From: cidrblock Date: Sat, 11 Dec 2021 12:37:01 -0800 Subject: [PATCH 44/59] rm merge conflict --- src/ansible_navigator/utils.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/ansible_navigator/utils.py b/src/ansible_navigator/utils.py index 4d655e10b..ce9f8d319 100644 --- a/src/ansible_navigator/utils.py +++ b/src/ansible_navigator/utils.py @@ -349,11 +349,7 @@ def divmod_int(numerator: Union[int, float], denominator: Union[int, float]) -> return int(quotient), int(remainder) -<<<<<<< HEAD def human_time(seconds: Union[int, float]) -> str: -======= -def human_time(seconds: int) -> str: ->>>>>>> 7eb4ca9 (Update docstrings) """Convert seconds into human readable 00d00h00m00s format.""" sign_string = "-" if seconds < 0 else "" seconds = abs(int(seconds)) From 5889d97891dd1a7f981ef5bd11d7f213d9157bdd Mon Sep 17 00:00:00 2001 From: cidrblock Date: Sat, 11 Dec 2021 12:44:02 -0800 Subject: [PATCH 45/59] empty From f34c4fbedcb12d20ffe7b3255e8812a6c44d406f Mon Sep 17 00:00:00 2001 From: cidrblock Date: Sun, 12 Dec 2021 05:53:09 -0800 Subject: [PATCH 46/59] rm unneeded pylint --- tests/unit/actions/test_exec.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/unit/actions/test_exec.py b/tests/unit/actions/test_exec.py index 01a2bb9a0..c649d70b3 100644 --- a/tests/unit/actions/test_exec.py +++ b/tests/unit/actions/test_exec.py @@ -78,7 +78,6 @@ def test_artifact_path(cmd_test_data: CommandTestData): :param cmd_test_data: The test data """ - # pylint: disable=protected-access command, additional_params = _generate_command( exec_command=cmd_test_data.command, exec_shell=cmd_test_data.shell, From de19ffec4050b7012227344885b1b7fb4544fbba Mon Sep 17 00:00:00 2001 From: cidrblock Date: Sat, 11 Dec 2021 12:44:02 -0800 Subject: [PATCH 47/59] empty From 8a661d0276469330dfab96f0eb2c14c46f086d5f Mon Sep 17 00:00:00 2001 From: cidrblock Date: Sun, 12 Dec 2021 07:05:24 -0800 Subject: [PATCH 48/59] empty From 73744b7109ed59af9eaad415ff5b0b86cb619705 Mon Sep 17 00:00:00 2001 From: "Bradley A. Thornton" Date: Mon, 13 Dec 2021 04:14:53 -0800 Subject: [PATCH 49/59] Update docs/changelog-fragments.d/526.feature.md Co-authored-by: Sviatoslav Sydorenko --- docs/changelog-fragments.d/526.feature.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/changelog-fragments.d/526.feature.md b/docs/changelog-fragments.d/526.feature.md index 2f8e10bae..05abfbaec 100644 --- a/docs/changelog-fragments.d/526.feature.md +++ b/docs/changelog-fragments.d/526.feature.md @@ -1,8 +1,9 @@ Added the ability to run ad-hoc commands directly within an execution environment using mode `stdout`. -``` +```console $ ansible-navigator exec --mode stdout +[example output] ``` -- by {user}`cidrblock` From 0c8c06cc9c3a57fcbe54e05497814c58a8d48eff Mon Sep 17 00:00:00 2001 From: "Bradley A. Thornton" Date: Mon, 13 Dec 2021 04:16:10 -0800 Subject: [PATCH 50/59] Update docs/changelog-fragments.d/526.feature.md Co-authored-by: Sviatoslav Sydorenko --- docs/changelog-fragments.d/526.feature.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog-fragments.d/526.feature.md b/docs/changelog-fragments.d/526.feature.md index 05abfbaec..cea54c301 100644 --- a/docs/changelog-fragments.d/526.feature.md +++ b/docs/changelog-fragments.d/526.feature.md @@ -1,6 +1,7 @@ Added the ability to run ad-hoc commands directly within an execution environment using mode `stdout`. +Here is an example invocation: ```console $ ansible-navigator exec --mode stdout [example output] From 0c508687fd4ae5095dab2679d466d5e83af71416 Mon Sep 17 00:00:00 2001 From: cidrblock Date: Mon, 13 Dec 2021 04:20:17 -0800 Subject: [PATCH 51/59] no output --- docs/changelog-fragments.d/526.feature.md | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/changelog-fragments.d/526.feature.md b/docs/changelog-fragments.d/526.feature.md index cea54c301..d98c57d9e 100644 --- a/docs/changelog-fragments.d/526.feature.md +++ b/docs/changelog-fragments.d/526.feature.md @@ -4,7 +4,6 @@ environment using mode `stdout`. Here is an example invocation: ```console $ ansible-navigator exec --mode stdout -[example output] ``` -- by {user}`cidrblock` From 3009fc81d5244d153ac57768838f86afd12faf7c Mon Sep 17 00:00:00 2001 From: cidrblock Date: Mon, 13 Dec 2021 04:23:33 -0800 Subject: [PATCH 52/59] no prompt --- docs/changelog-fragments.d/526.feature.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog-fragments.d/526.feature.md b/docs/changelog-fragments.d/526.feature.md index d98c57d9e..7d5bc5142 100644 --- a/docs/changelog-fragments.d/526.feature.md +++ b/docs/changelog-fragments.d/526.feature.md @@ -3,7 +3,7 @@ environment using mode `stdout`. Here is an example invocation: ```console -$ ansible-navigator exec --mode stdout +ansible-navigator exec --mode stdout ``` -- by {user}`cidrblock` From 9353a4883ebba4b5fb1ce8ac055237cf6a80ea2a Mon Sep 17 00:00:00 2001 From: "Bradley A. Thornton" Date: Mon, 13 Dec 2021 16:28:18 -0800 Subject: [PATCH 53/59] Update docs/changelog-fragments.d/526.feature.md Co-authored-by: Sviatoslav Sydorenko --- docs/changelog-fragments.d/526.feature.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog-fragments.d/526.feature.md b/docs/changelog-fragments.d/526.feature.md index 7d5bc5142..f7b45685a 100644 --- a/docs/changelog-fragments.d/526.feature.md +++ b/docs/changelog-fragments.d/526.feature.md @@ -2,7 +2,7 @@ Added the ability to run ad-hoc commands directly within an execution environment using mode `stdout`. Here is an example invocation: -```console +```bash ansible-navigator exec --mode stdout ``` From d5f6e33fdaaa1b1b0234f6ee0ed76e330fae04e7 Mon Sep 17 00:00:00 2001 From: cidrblock Date: Sat, 11 Dec 2021 12:44:02 -0800 Subject: [PATCH 54/59] empty From a2c13cf2f9fc28482b36baa8d9a756265fc5fd98 Mon Sep 17 00:00:00 2001 From: cidrblock Date: Sat, 11 Dec 2021 12:44:02 -0800 Subject: [PATCH 55/59] empty From e0c914bfab33206dd69691f567831bf9993a1558 Mon Sep 17 00:00:00 2001 From: cidrblock Date: Sun, 12 Dec 2021 07:05:24 -0800 Subject: [PATCH 56/59] empty From 0ebf309e41c56714ab64af4c236b28d9e6bbda20 Mon Sep 17 00:00:00 2001 From: "Bradley A. Thornton" Date: Tue, 14 Dec 2021 07:27:13 -0800 Subject: [PATCH 57/59] Update src/ansible_navigator/actions/exec.py Co-authored-by: Sviatoslav Sydorenko --- src/ansible_navigator/actions/exec.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ansible_navigator/actions/exec.py b/src/ansible_navigator/actions/exec.py index 045573853..9a628c38b 100644 --- a/src/ansible_navigator/actions/exec.py +++ b/src/ansible_navigator/actions/exec.py @@ -76,7 +76,7 @@ def _run_runner(self) -> Optional[Tuple]: else: log_message = ( "The setting 'set_environment_variable' was neither a dictionary" - + " or Constants, please raise an issue. No environment variables will be set." + " or Constants, please raise an issue. No environment variables will be set." ) self._logger.error( "%s The current value was found to be '%s'", From 945cde6a85a699411d590d0c1ab7386cec11a9f5 Mon Sep 17 00:00:00 2001 From: cidrblock Date: Tue, 14 Dec 2021 07:44:28 -0800 Subject: [PATCH 58/59] rm try/except in action --- src/ansible_navigator/actions/_actions.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/ansible_navigator/actions/_actions.py b/src/ansible_navigator/actions/_actions.py index 9ad3c42d3..26660b201 100644 --- a/src/ansible_navigator/actions/_actions.py +++ b/src/ansible_navigator/actions/_actions.py @@ -98,11 +98,12 @@ def run_interactive(package: str, action: str, *args: Any, **_kwargs: Any) -> An """Call the given action's run""" action_cls = get(package, action) app, interaction = args - try: - return action_cls(app.args).run(app=app, interaction=interaction) - except AttributeError: + app_action = action_cls(app.args) + supports_interactive = hasattr(app_action, "run") + if not supports_interactive: logger.error("Subcommand '%s' does not support mode interactive", action) - return action_cls(app.args).no_interactive_mode(app=app, interaction=interaction) + run_action = app_action.run if supports_interactive else app_action.no_interactive_mode + return run_action(app=app, interaction=interaction) def run_interactive_factory(package: str) -> Callable: From 74ff5de7d7091bb1b9ef99fd7f176c7adb0a0f77 Mon Sep 17 00:00:00 2001 From: "Bradley A. Thornton" Date: Tue, 14 Dec 2021 08:00:31 -0800 Subject: [PATCH 59/59] Update tests/integration/actions/exec/base.py Co-authored-by: Sviatoslav Sydorenko --- tests/integration/actions/exec/base.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/integration/actions/exec/base.py b/tests/integration/actions/exec/base.py index f9f1b1680..71fd0e315 100644 --- a/tests/integration/actions/exec/base.py +++ b/tests/integration/actions/exec/base.py @@ -59,11 +59,11 @@ def test(self, request: pytest.FixtureRequest, tmux_session: TmuxSession, step: :param step: A step within a series of tests :raises ValueError: If test mode isn't set """ - if step.search_within_response is SearchFor.PROMPT: - search_within_response = tmux_session.cli_prompt - else: + if step.search_within_response is not SearchFor.PROMPT: raise ValueError("test mode not set") + search_within_response = tmux_session.cli_prompt + received_output = tmux_session.interaction( value=step.user_input, search_within_response=search_within_response,