Skip to content

Commit

Permalink
Handle errors in nested operations like unnested operations
Browse files Browse the repository at this point in the history
This correctly fails hosts when they fail executing a nested operation,
just like unnested operations.
  • Loading branch information
Fizzadar committed Feb 25, 2024
1 parent 924e644 commit 87e73c6
Show file tree
Hide file tree
Showing 4 changed files with 15 additions and 6 deletions.
6 changes: 6 additions & 0 deletions pyinfra/api/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ class PyinfraError(Exception):
"""


class NoMoreHostsError(PyinfraError):
"""
Exception raised when pyinfra runs out of hosts (they all failed).
"""


class ConnectError(PyinfraError):
"""
Exception raised when connecting fails.
Expand Down
4 changes: 3 additions & 1 deletion pyinfra/api/operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,9 @@ def _execute_immediately(state, host, op_data, op_meta, op_hash):
)
op_data["parent_op_hash"] = host.executing_op_hash
log_operation_start(op_meta, op_types=["nested"], prefix="")
run_host_op(state, host, op_hash)
status = run_host_op(state, host, op_hash)
if status is False:
state.fail_hosts({host})


def _attach_args(op_meta, args, kwargs):
Expand Down
5 changes: 3 additions & 2 deletions pyinfra/api/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from .arguments import get_executor_kwarg_keys
from .command import FunctionCommand, PyinfraCommand, StringCommand
from .exceptions import PyinfraError
from .exceptions import NoMoreHostsError, PyinfraError
from .util import (
format_exception,
log_error_or_warning,
Expand Down Expand Up @@ -115,7 +115,6 @@ def run_condition(condition_name: str) -> bool:
all_combined_output_lines = []

for i, command in enumerate(op_data["commands"]):

status = False

executor_kwargs = base_executor_kwargs.copy()
Expand All @@ -130,6 +129,8 @@ def run_condition(condition_name: str) -> bool:
if isinstance(command, FunctionCommand):
try:
status = command.execute(state, host, executor_kwargs)
except NoMoreHostsError:
status = False
except Exception as e: # Custom functions could do anything, so expect anything!
_formatted_exc = format_exception(e)
_error_msg = "Unexpected error in Python callback: {0}".format(_formatted_exc)
Expand Down
6 changes: 3 additions & 3 deletions pyinfra/api/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from pyinfra import logger

from .config import Config
from .exceptions import PyinfraError
from .exceptions import NoMoreHostsError, PyinfraError
from .util import sha1_hash

if TYPE_CHECKING:
Expand Down Expand Up @@ -330,13 +330,13 @@ def fail_hosts(self, hosts_to_fail, activated_count=None):

# No hosts left!
if not active_hosts:
raise PyinfraError("No hosts remaining!")
raise NoMoreHostsError("No hosts remaining!")

if self.config.FAIL_PERCENT is not None:
percent_failed = (1 - len(active_hosts) / activated_count) * 100

if percent_failed > self.config.FAIL_PERCENT:
raise PyinfraError(
raise NoMoreHostsError(
"Over {0}% of hosts failed ({1}%)".format(
self.config.FAIL_PERCENT,
int(round(percent_failed)),
Expand Down

0 comments on commit 87e73c6

Please sign in to comment.