diff --git a/tdp/cli/commands/deploy.py b/tdp/cli/commands/deploy.py index 53e274af..1b10f241 100644 --- a/tdp/cli/commands/deploy.py +++ b/tdp/cli/commands/deploy.py @@ -15,7 +15,7 @@ validate, vars, ) -from tdp.core.deployment import AnsibleExecutor, DeploymentPlan, DeploymentRunner +from tdp.core.deployment import Executor, DeploymentPlan, DeploymentRunner from tdp.core.models import DeploymentStateEnum from tdp.core.variables import ClusterVariables @@ -47,7 +47,7 @@ def deploy( deployment_runner = DeploymentRunner( collections, - AnsibleExecutor( + Executor( run_directory=run_directory.absolute() if run_directory else None, dry=dry or mock_deploy, ), diff --git a/tdp/core/deployment/__init__.py b/tdp/core/deployment/__init__.py index 18080524..6b766f7d 100644 --- a/tdp/core/deployment/__init__.py +++ b/tdp/core/deployment/__init__.py @@ -1,7 +1,6 @@ # Copyright 2022 TOSIT.IO # SPDX-License-Identifier: Apache-2.0 -from .ansible_executor import AnsibleExecutor from .deployment_iterator import DeploymentIterator from .deployment_plan import ( DeploymentPlan, diff --git a/tdp/core/deployment/ansible_executor.py b/tdp/core/deployment/ansible_executor.py deleted file mode 100644 index 466cea64..00000000 --- a/tdp/core/deployment/ansible_executor.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright 2022 TOSIT.IO -# SPDX-License-Identifier: Apache-2.0 - -import io -import logging -import subprocess -from typing import Tuple - -from tdp.core.models import OperationStateEnum - -from .executor import Executor - -logger = logging.getLogger("tdp").getChild("ansible_executor") - - -class AnsibleExecutor(Executor): - """Executor that runs ansible commands.""" - - def __init__(self, run_directory=None, dry: bool = False): - """Initialize the executor. - - Args: - run_directory: Directory where to run the ansible command. - dry: Whether or not to run the command in dry mode. - """ - # TODO configurable via config file - self._rundir = run_directory - self._dry = dry - - def _execute_ansible_command(self, command: str) -> Tuple[OperationStateEnum, str]: - """Execute an ansible command. - - Args: - command: Command to execute. - - Returns: - A tuple with the state of the command and the output of the command. - """ - with io.BytesIO() as byte_stream: - try: - res = subprocess.Popen( - command, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - cwd=self._rundir, - universal_newlines=True, - ) - if res.stdout is None: - raise Exception("Process has not stdout") - for stdout_line in iter(res.stdout.readline, ""): - print(stdout_line, end="") - byte_stream.write(bytes(stdout_line, "utf-8")) - state = ( - OperationStateEnum.SUCCESS - if res.wait() == 0 - else OperationStateEnum.FAILURE - ) - except KeyboardInterrupt: - logger.debug("KeyboardInterrupt caught") - byte_stream.write(b"\nKeyboardInterrupt") - return OperationStateEnum.FAILURE, byte_stream.getvalue() - return state, byte_stream.getvalue() - - def execute(self, operation: str) -> Tuple[OperationStateEnum, str]: - command = ["ansible-playbook", str(operation)] - if self._dry: - logger.info("[DRY MODE] Ansible command: " + " ".join(command)) - return OperationStateEnum.SUCCESS, b"" - return self._execute_ansible_command(command) diff --git a/tdp/core/deployment/executor.py b/tdp/core/deployment/executor.py index 8c4a0d77..ea23b799 100644 --- a/tdp/core/deployment/executor.py +++ b/tdp/core/deployment/executor.py @@ -1,23 +1,75 @@ # Copyright 2022 TOSIT.IO # SPDX-License-Identifier: Apache-2.0 -from abc import ABC, abstractmethod +import io +import logging +import subprocess from typing import Tuple from tdp.core.models import OperationStateEnum +logger = logging.getLogger("tdp").getChild("ansible_executor") -class Executor(ABC): - """An Executor is an object able to run operations.""" - @abstractmethod - def execute(self, operation: str) -> Tuple[OperationStateEnum, bytes]: +class Executor: + """Allow to execute commands using Ansible.""" + + def __init__(self, run_directory=None, dry: bool = False): + """Initialize the executor. + + Args: + run_directory: Directory where to run the ansible command. + dry: Whether or not to run the command in dry mode. + """ + # TODO configurable via config file + self._rundir = run_directory + self._dry = dry + + def _execute_ansible_command(self, command: str) -> Tuple[OperationStateEnum, str]: + """Execute an ansible command. + + Args: + command: Command to execute. + + Returns: + A tuple with the state of the command and the output of the command. + """ + with io.BytesIO() as byte_stream: + try: + res = subprocess.Popen( + command, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + cwd=self._rundir, + universal_newlines=True, + ) + if res.stdout is None: + raise Exception("Process has not stdout") + for stdout_line in iter(res.stdout.readline, ""): + print(stdout_line, end="") + byte_stream.write(bytes(stdout_line, "utf-8")) + state = ( + OperationStateEnum.SUCCESS + if res.wait() == 0 + else OperationStateEnum.FAILURE + ) + except KeyboardInterrupt: + logger.debug("KeyboardInterrupt caught") + byte_stream.write(b"\nKeyboardInterrupt") + return OperationStateEnum.FAILURE, byte_stream.getvalue() + return state, byte_stream.getvalue() + + def execute(self, operation: str) -> Tuple[OperationStateEnum, str]: """Executes an operation. Args: - operation: Operation name. + operation: Name of the operation to execute. Returns: - Whether an operation is a success as well as its logs in UTF-8 bytes. + A tuple with the state of the command and the output of the command in UTF-8. """ - pass + command = ["ansible-playbook", str(operation)] + if self._dry: + logger.info("[DRY MODE] Ansible command: " + " ".join(command)) + return OperationStateEnum.SUCCESS, b"" + return self._execute_ansible_command(command) diff --git a/tests/local/test_action_runner.py b/tests/local/test_action_runner.py index 8b1f4caa..370e7b3a 100644 --- a/tests/local/test_action_runner.py +++ b/tests/local/test_action_runner.py @@ -11,7 +11,7 @@ from tdp.core.models import Base, OperationStateEnum from tdp.core.models.deployment_log import DeploymentLog from tdp.core.runner.action_runner import ActionRunner -from tdp.core.runner.executor import Executor +from tdp.core.deployment import Executor logger = logging.getLogger("tdp").getChild("test_action_runner")