Add systemd timer fallback when crontab unavailable
All checks were successful
Build and Push Container / build (push) Successful in 33s

- Try crontab first, fall back to systemd timer
- If neither available, still deploy script but warn user
- Update frontend to show scheduling method (cron/systemd/none)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Debian
2026-01-05 10:08:04 +00:00
parent ebaadbdffa
commit 6865f3c9a4
2 changed files with 71 additions and 11 deletions

View File

@@ -298,22 +298,61 @@ class MonitorService:
"error": f"Failed to make script executable: {error_detail}", "error": f"Failed to make script executable: {error_detail}",
} }
# Add cronjob entry (remove existing entry first to avoid duplicates) # Try to add cronjob entry (crontab may not be available on all systems)
cron_cmd = f"(crontab -l 2>/dev/null | grep -v '{script_filename}'; echo '{cronjob_entry}') | crontab -" cron_cmd = f"(crontab -l 2>/dev/null | grep -v '{script_filename}'; echo '{cronjob_entry}') | crontab -"
cron_result = ssh.execute(hostname, cron_cmd, username, port) cron_result = ssh.execute(hostname, cron_cmd, username, port)
if not cron_result.success:
return { scheduling_method = None
"status": "failed", scheduling_info = None
"error": f"Failed to add cronjob: {cron_result.stderr}",
} if cron_result.success:
scheduling_method = "crontab"
scheduling_info = cronjob_entry
else:
# Try systemd timer as fallback
timer_name = f"kuma-push-{push_metric}-{monitor_id}"
timer_content = f"""[Unit]
Description=Kuma Push Monitor - {push_metric}
[Timer]
OnBootSec=1min
OnUnitActiveSec={interval_minutes}min
AccuracySec=1s
[Install]
WantedBy=timers.target
"""
service_content = f"""[Unit]
Description=Kuma Push Monitor - {push_metric}
[Service]
Type=oneshot
ExecStart={script_path}
"""
# Write timer and service files
timer_path = f"/etc/systemd/system/{timer_name}.timer"
service_path = f"/etc/systemd/system/{timer_name}.service"
timer_cmd = f"sudo tee {timer_path} > /dev/null << 'KUMA_TIMER_EOF'\n{timer_content}KUMA_TIMER_EOF"
service_cmd = f"sudo tee {service_path} > /dev/null << 'KUMA_SERVICE_EOF'\n{service_content}KUMA_SERVICE_EOF"
timer_result = ssh.execute(hostname, timer_cmd, username, port)
if timer_result.success:
service_result = ssh.execute(hostname, service_cmd, username, port)
if service_result.success:
# Enable and start the timer
enable_cmd = f"sudo systemctl daemon-reload && sudo systemctl enable --now {timer_name}.timer"
enable_result = ssh.execute(hostname, enable_cmd, username, port)
if enable_result.success:
scheduling_method = "systemd"
scheduling_info = f"{timer_name}.timer"
# Run the script once immediately to verify it works # Run the script once immediately to verify it works
run_result = ssh.execute(hostname, script_path, username, port, timeout=30) run_result = ssh.execute(hostname, script_path, username, port, timeout=30)
return { result = {
"status": "deployed", "status": "deployed",
"script_path": script_path, "script_path": script_path,
"cronjob": cronjob_entry,
"initial_run": { "initial_run": {
"success": run_result.success, "success": run_result.success,
"stdout": run_result.stdout, "stdout": run_result.stdout,
@@ -321,6 +360,19 @@ class MonitorService:
}, },
} }
if scheduling_method:
result["scheduling"] = {
"method": scheduling_method,
"info": scheduling_info,
}
else:
result["scheduling"] = {
"method": "none",
"warning": "Neither crontab nor systemd available. Script deployed but not scheduled.",
}
return result
except Exception as e: except Exception as e:
logger.exception(f"Failed to deploy push script to {hostname}") logger.exception(f"Failed to deploy push script to {hostname}")
return { return {

View File

@@ -409,9 +409,17 @@ export default function DiscoveryResults({ scanId, scan, analysis, devMode, onCo
</> </>
)} )}
</div> </div>
{result.deployment.cronjob && ( {result.deployment.scheduling && (
<div className="mt-1 text-xs text-slate-500 font-mono"> <div className="mt-1 text-xs text-slate-500">
Cronjob: {result.deployment.cronjob} {result.deployment.scheduling.method === 'crontab' && (
<span className="font-mono">Cron: {result.deployment.scheduling.info}</span>
)}
{result.deployment.scheduling.method === 'systemd' && (
<span>Systemd timer: {result.deployment.scheduling.info}</span>
)}
{result.deployment.scheduling.method === 'none' && (
<span className="text-amber-400">{result.deployment.scheduling.warning}</span>
)}
</div> </div>
)} )}
</div> </div>