GRM-17: fix: make grm list retrieve runner status correctly
- run_ad_hoc() now raises AnsibleError on non-zero exit, surfacing stderr instead of silently returning empty stdout - list_runners() passes become=True to run_ad_hoc since systemctl is-active requires root privileges - Add i18n translations for ad-hoc failure messages - Add unit test for run_ad_hoc failure case - Update list_runners test to expect become=True - 119 tests, 100% coverage, pyright clean, ruff clean
This commit is contained in:
@@ -109,6 +109,15 @@ class AnsibleExecutor:
|
|||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
)
|
)
|
||||||
|
if proc.returncode != 0:
|
||||||
|
stderr = proc.stderr.strip() if proc.stderr else ""
|
||||||
|
raise AnsibleError(
|
||||||
|
_(
|
||||||
|
"Ad-hoc command failed on {host}: {stderr}",
|
||||||
|
host=host,
|
||||||
|
stderr=stderr or _("exit code {code}", code=proc.returncode),
|
||||||
|
)
|
||||||
|
)
|
||||||
return proc.stdout.strip()
|
return proc.stdout.strip()
|
||||||
|
|
||||||
def _extract_status(self, log_file: Path) -> str | None:
|
def _extract_status(self, log_file: Path) -> str | None:
|
||||||
|
|||||||
@@ -474,6 +474,20 @@ TRANSLATIONS: dict[str, dict[str, str]] = {
|
|||||||
"ru": "Пропустить удаленную очистку и удалить только локальную запись реестра",
|
"ru": "Пропустить удаленную очистку и удалить только локальную запись реестра",
|
||||||
"zh": "跳过远程清理,仅删除本地注册表条目",
|
"zh": "跳过远程清理,仅删除本地注册表条目",
|
||||||
},
|
},
|
||||||
|
"Ad-hoc command failed on {host}: {stderr}": {
|
||||||
|
"en": "Ad-hoc command failed on {host}: {stderr}",
|
||||||
|
"bg": "Ad-hoc командата неуспешна на {host}: {stderr}",
|
||||||
|
"de": "Ad-hoc-Befehl auf {host} fehlgeschlagen: {stderr}",
|
||||||
|
"ru": "Ad-hoc команда не удалась на {host}: {stderr}",
|
||||||
|
"zh": "Ad-hoc 命令在 {host} 上失败: {stderr}",
|
||||||
|
},
|
||||||
|
"exit code {code}": {
|
||||||
|
"en": "exit code {code}",
|
||||||
|
"bg": "код за изход {code}",
|
||||||
|
"de": "Exit-Code {code}",
|
||||||
|
"ru": "код выхода {code}",
|
||||||
|
"zh": "退出代码 {code}",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -281,7 +281,9 @@ class RunnerManager:
|
|||||||
key = info.get("key")
|
key = info.get("key")
|
||||||
mode = info.get("mode", "docker")
|
mode = info.get("mode", "docker")
|
||||||
try:
|
try:
|
||||||
stdout = self._executor.run_ad_hoc(host, user, key, "shell", f"systemctl is-active gitea-runner@{name}")
|
stdout = self._executor.run_ad_hoc(
|
||||||
|
host, user, key, "shell", f"systemctl is-active gitea-runner@{name}", become=True
|
||||||
|
)
|
||||||
# Filter out ansible noise (SSH warnings etc)
|
# Filter out ansible noise (SSH warnings etc)
|
||||||
lines = [ln for ln in stdout.splitlines() if ln.strip() and not ln.startswith(("[WARNING]", "ssh:"))]
|
lines = [ln for ln in stdout.splitlines() if ln.strip() and not ln.startswith(("[WARNING]", "ssh:"))]
|
||||||
service_status = lines[-1].strip() if lines else "unknown"
|
service_status = lines[-1].strip() if lines else "unknown"
|
||||||
|
|||||||
@@ -206,3 +206,14 @@ class TestAnsibleExecutorAdHoc:
|
|||||||
assert result == "ok"
|
assert result == "ok"
|
||||||
cmd = mock_run.call_args.args[0]
|
cmd = mock_run.call_args.args[0]
|
||||||
assert "--become" in cmd
|
assert "--become" in cmd
|
||||||
|
|
||||||
|
def test_run_ad_hoc_failure_raises(self, tmp_path: Path) -> None:
|
||||||
|
executor = AnsibleExecutor(log_dir=tmp_path)
|
||||||
|
result_mock = MagicMock()
|
||||||
|
result_mock.stdout = ""
|
||||||
|
result_mock.returncode = 1
|
||||||
|
result_mock.stderr = "SSH timeout"
|
||||||
|
|
||||||
|
with patch("subprocess.run", return_value=result_mock):
|
||||||
|
with pytest.raises(AnsibleError, match="SSH timeout"):
|
||||||
|
executor.run_ad_hoc("10.0.0.1", "ubuntu", None, "shell", "cmd")
|
||||||
|
|||||||
@@ -408,7 +408,7 @@ class TestRunnerManager:
|
|||||||
assert runners[0]["host"] == "10.0.0.1"
|
assert runners[0]["host"] == "10.0.0.1"
|
||||||
assert runners[0]["status"] == "active"
|
assert runners[0]["status"] == "active"
|
||||||
mock_executor.run_ad_hoc.assert_called_once_with(
|
mock_executor.run_ad_hoc.assert_called_once_with(
|
||||||
"10.0.0.1", "ubuntu", "/key", "shell", "systemctl is-active gitea-runner@r1"
|
"10.0.0.1", "ubuntu", "/key", "shell", "systemctl is-active gitea-runner@r1", become=True
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_list_runners_empty(self) -> None:
|
def test_list_runners_empty(self) -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user