Skip to content

Commit 87e73c6

Browse files
committed
Handle errors in nested operations like unnested operations
This correctly fails hosts when they fail executing a nested operation, just like unnested operations.
1 parent 924e644 commit 87e73c6

File tree

4 files changed

+15
-6
lines changed

4 files changed

+15
-6
lines changed

pyinfra/api/exceptions.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ class PyinfraError(Exception):
44
"""
55

66

7+
class NoMoreHostsError(PyinfraError):
8+
"""
9+
Exception raised when pyinfra runs out of hosts (they all failed).
10+
"""
11+
12+
713
class ConnectError(PyinfraError):
814
"""
915
Exception raised when connecting fails.

pyinfra/api/operation.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,9 @@ def _execute_immediately(state, host, op_data, op_meta, op_hash):
343343
)
344344
op_data["parent_op_hash"] = host.executing_op_hash
345345
log_operation_start(op_meta, op_types=["nested"], prefix="")
346-
run_host_op(state, host, op_hash)
346+
status = run_host_op(state, host, op_hash)
347+
if status is False:
348+
state.fail_hosts({host})
347349

348350

349351
def _attach_args(op_meta, args, kwargs):

pyinfra/api/operations.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
from .arguments import get_executor_kwarg_keys
1616
from .command import FunctionCommand, PyinfraCommand, StringCommand
17-
from .exceptions import PyinfraError
17+
from .exceptions import NoMoreHostsError, PyinfraError
1818
from .util import (
1919
format_exception,
2020
log_error_or_warning,
@@ -115,7 +115,6 @@ def run_condition(condition_name: str) -> bool:
115115
all_combined_output_lines = []
116116

117117
for i, command in enumerate(op_data["commands"]):
118-
119118
status = False
120119

121120
executor_kwargs = base_executor_kwargs.copy()
@@ -130,6 +129,8 @@ def run_condition(condition_name: str) -> bool:
130129
if isinstance(command, FunctionCommand):
131130
try:
132131
status = command.execute(state, host, executor_kwargs)
132+
except NoMoreHostsError:
133+
status = False
133134
except Exception as e: # Custom functions could do anything, so expect anything!
134135
_formatted_exc = format_exception(e)
135136
_error_msg = "Unexpected error in Python callback: {0}".format(_formatted_exc)

pyinfra/api/state.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from pyinfra import logger
1111

1212
from .config import Config
13-
from .exceptions import PyinfraError
13+
from .exceptions import NoMoreHostsError, PyinfraError
1414
from .util import sha1_hash
1515

1616
if TYPE_CHECKING:
@@ -330,13 +330,13 @@ def fail_hosts(self, hosts_to_fail, activated_count=None):
330330

331331
# No hosts left!
332332
if not active_hosts:
333-
raise PyinfraError("No hosts remaining!")
333+
raise NoMoreHostsError("No hosts remaining!")
334334

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

338338
if percent_failed > self.config.FAIL_PERCENT:
339-
raise PyinfraError(
339+
raise NoMoreHostsError(
340340
"Over {0}% of hosts failed ({1}%)".format(
341341
self.config.FAIL_PERCENT,
342342
int(round(percent_failed)),

0 commit comments

Comments
 (0)