GRM-17: fix docker mode status detection and systemd template
- systemd template for docker mode now uses Type=oneshot + RemainAfterExit=yes
so that systemctl is-active returns active when the container is running.
Previously docker run -d exited immediately, causing systemd to mark the
service as inactive even though the container was still up.
- grm list now tries multiple container name fallbacks for docker mode:
1. gitea-runner-{name} (current naming)
2. gitea-runner-{host} (legacy installs where name defaulted to host)
3. systemctl is-active gitea-runner@{name} (for installs with fixed template)
- All tests pass, 100% coverage, ruff + pyright clean
This commit is contained in:
@@ -4,8 +4,9 @@ After=network.target{% if runner_mode == 'docker' %} docker.service
|
|||||||
Requires=docker.service{% endif %}
|
Requires=docker.service{% endif %}
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
|
||||||
{% if runner_mode == 'docker' %}
|
{% if runner_mode == 'docker' %}
|
||||||
|
Type=oneshot
|
||||||
|
RemainAfterExit=yes
|
||||||
ExecStartPre=-/usr/bin/docker rm -f gitea-runner-%i
|
ExecStartPre=-/usr/bin/docker rm -f gitea-runner-%i
|
||||||
ExecStart=/usr/bin/docker run -d --name gitea-runner-%i \
|
ExecStart=/usr/bin/docker run -d --name gitea-runner-%i \
|
||||||
--restart=no \
|
--restart=no \
|
||||||
@@ -19,6 +20,7 @@ ExecStopPost=-/usr/bin/docker rm -f gitea-runner-%i
|
|||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
RestartSec={{ gitea_runner_service_restart_sec }}
|
RestartSec={{ gitea_runner_service_restart_sec }}
|
||||||
{% else %}
|
{% else %}
|
||||||
|
Type=simple
|
||||||
ExecStart={{ gitea_runner_binary_path }} daemon --config {{ gitea_runner_base_config_dir }}/%i/config.yaml
|
ExecStart={{ gitea_runner_binary_path }} daemon --config {{ gitea_runner_base_config_dir }}/%i/config.yaml
|
||||||
WorkingDirectory={{ gitea_runner_base_data_dir }}/%i
|
WorkingDirectory={{ gitea_runner_base_data_dir }}/%i
|
||||||
ExecStop=/bin/kill -TERM $MAINPID
|
ExecStop=/bin/kill -TERM $MAINPID
|
||||||
|
|||||||
@@ -289,18 +289,44 @@ class RunnerManager:
|
|||||||
user=user,
|
user=user,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def _parse_status(stdout: str) -> str:
|
||||||
|
ansible_noise = (" | CHANGED | ", " | FAILED | ", " | UNREACHABLE | ", "[WARNING]", "ssh:", ">>")
|
||||||
|
lines = [ln for ln in stdout.splitlines() if ln.strip() and not any(p in ln for p in ansible_noise)]
|
||||||
|
return lines[-1].strip() if lines else "unknown"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if mode == "docker":
|
if mode == "docker":
|
||||||
stdout = self._executor.run_ad_hoc(
|
# Try expected container name first, then host-based fallback for legacy installs
|
||||||
host,
|
for container_name in (f"gitea-runner-{name}", f"gitea-runner-{host}"):
|
||||||
user,
|
stdout = self._executor.run_ad_hoc(
|
||||||
key,
|
host,
|
||||||
"shell",
|
user,
|
||||||
f"docker inspect -f '{{{{.State.Status}}}}' gitea-runner-{name} 2>/dev/null",
|
key,
|
||||||
become=True,
|
"shell",
|
||||||
ask_become_pass=True,
|
f"docker inspect -f '{{{{.State.Status}}}}' {container_name} 2>/dev/null",
|
||||||
check=False,
|
become=True,
|
||||||
)
|
ask_become_pass=True,
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
service_status = _parse_status(stdout)
|
||||||
|
if service_status != "unknown":
|
||||||
|
docker_map = {"running": "active", "exited": "inactive", "dead": "failed"}
|
||||||
|
service_status = docker_map.get(service_status, "unknown")
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# Final fallback: check systemd service (for installs using the fixed template)
|
||||||
|
stdout = self._executor.run_ad_hoc(
|
||||||
|
host,
|
||||||
|
user,
|
||||||
|
key,
|
||||||
|
"shell",
|
||||||
|
f"systemctl is-active gitea-runner@{name} 2>/dev/null",
|
||||||
|
become=True,
|
||||||
|
ask_become_pass=True,
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
service_status = _parse_status(stdout)
|
||||||
else:
|
else:
|
||||||
stdout = self._executor.run_ad_hoc(
|
stdout = self._executor.run_ad_hoc(
|
||||||
host,
|
host,
|
||||||
@@ -312,14 +338,7 @@ class RunnerManager:
|
|||||||
ask_become_pass=True,
|
ask_become_pass=True,
|
||||||
check=False,
|
check=False,
|
||||||
)
|
)
|
||||||
# Filter out ansible noise (headers, warnings, SSH messages)
|
service_status = _parse_status(stdout)
|
||||||
ansible_noise = (" | CHANGED | ", " | FAILED | ", " | UNREACHABLE | ", "[WARNING]", "ssh:", ">>")
|
|
||||||
lines = [ln for ln in stdout.splitlines() if ln.strip() and not any(p in ln for p in ansible_noise)]
|
|
||||||
service_status = lines[-1].strip() if lines else "unknown"
|
|
||||||
# Docker statuses need mapping to systemd vocabulary
|
|
||||||
if mode == "docker":
|
|
||||||
docker_map = {"running": "active", "exited": "inactive", "dead": "failed"}
|
|
||||||
service_status = docker_map.get(service_status, "unknown")
|
|
||||||
except Exception:
|
except Exception:
|
||||||
service_status = "unknown"
|
service_status = "unknown"
|
||||||
# Translate known status values
|
# Translate known status values
|
||||||
|
|||||||
Reference in New Issue
Block a user