Skip to content

Commit ee648c2

Browse files
committed
Add bounded retry around agent check invocations in env/agent.py
1 parent 996b3d5 commit ee648c2

1 file changed

Lines changed: 36 additions & 3 deletions

File tree

ddev/src/ddev/cli/env/agent.py

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,36 @@
99

1010
if TYPE_CHECKING:
1111
from ddev.cli.application import Application
12+
from ddev.e2e.agent.interface import AgentInterface
13+
14+
15+
def _invoke_check_with_retry(
16+
agent: AgentInterface, args: list[str], *, retries: int = 3, backoff: float = 0.5
17+
) -> None:
18+
"""Invoke ``agent check`` with bounded retry on non-zero exits.
19+
20+
E2E callers race the Agent's check loader: a config-swap or
21+
autodiscovery reload can briefly leave the check unregistered,
22+
causing ``agent check`` to exit with "no valid check found".
23+
Output streams live (not captured) so we can't filter by stderr;
24+
the bound ensures a genuinely broken check still surfaces.
25+
"""
26+
import subprocess
27+
import time
28+
29+
for attempt in range(retries + 1):
30+
try:
31+
agent.invoke(args)
32+
return
33+
except subprocess.CalledProcessError:
34+
if attempt >= retries:
35+
raise
36+
click.echo(
37+
f'agent check failed (attempt {attempt + 1}/{retries + 1}), '
38+
f'retrying in {backoff:.1f}s...',
39+
err=True,
40+
)
41+
time.sleep(backoff)
1242

1343

1444
@click.command(
@@ -54,7 +84,10 @@ def agent(app: Application, *, intg_name: str, environment: str, args: tuple[str
5484

5585
if config_file is None or not trigger_run:
5686
try:
57-
agent.invoke(full_args)
87+
if trigger_run:
88+
_invoke_check_with_retry(agent, full_args)
89+
else:
90+
agent.invoke(full_args)
5891
except subprocess.CalledProcessError as e:
5992
app.abort(code=e.returncode)
6093

@@ -67,14 +100,14 @@ def agent(app: Application, *, intg_name: str, environment: str, args: tuple[str
67100
if not env_data.config_file.is_file():
68101
try:
69102
env_data.write_config(config)
70-
agent.invoke(full_args)
103+
_invoke_check_with_retry(agent, full_args)
71104
finally:
72105
env_data.config_file.unlink()
73106
else:
74107
temp_config_file = env_data.config_file.parent / f'{env_data.config_file.name}.bak.example'
75108
env_data.config_file.replace(temp_config_file)
76109
try:
77110
env_data.write_config(config)
78-
agent.invoke(full_args)
111+
_invoke_check_with_retry(agent, full_args)
79112
finally:
80113
temp_config_file.replace(env_data.config_file)

0 commit comments

Comments
 (0)