nixfleet_agent/runtime/workers/probe_runners/
exec.rs1use chrono::{DateTime, Utc};
6use std::time::Duration;
7use tokio::process::Command;
8use tokio::time::timeout;
9
10use super::{ProbeDecl, RunnerOutcome};
11
12pub async fn run(decl: &ProbeDecl, now: DateTime<Utc>) -> RunnerOutcome {
13 if decl.command.is_empty() {
14 return RunnerOutcome::fail(now, "exec probe: command argv missing");
15 }
16 let mut cmd = Command::new(&decl.command[0]);
17 cmd.args(&decl.command[1..]);
18 cmd.stdin(std::process::Stdio::null());
19 cmd.stdout(std::process::Stdio::null());
20 cmd.stderr(std::process::Stdio::piped());
21
22 let child = match cmd.spawn() {
23 Ok(c) => c,
24 Err(err) => return RunnerOutcome::fail(now, format!("exec probe: spawn: {err}")),
25 };
26 match timeout(
27 Duration::from_secs(decl.timeout_secs),
28 child.wait_with_output(),
29 )
30 .await
31 {
32 Ok(Ok(out)) => {
33 if out.status.success() {
34 RunnerOutcome::pass(now)
35 } else {
36 let stderr_tail = String::from_utf8_lossy(&out.stderr);
37 let stderr_tail = stderr_tail
38 .lines()
39 .rev()
40 .take(3)
41 .collect::<Vec<_>>()
42 .join(" / ");
43 RunnerOutcome::fail(
44 now,
45 format!(
46 "exec probe: exit {:?}; stderr: {stderr_tail}",
47 out.status.code()
48 ),
49 )
50 }
51 }
52 Ok(Err(err)) => RunnerOutcome::fail(now, format!("exec probe: wait: {err}")),
53 Err(_elapsed) => RunnerOutcome::fail(
54 now,
55 format!("exec probe: timed out after {}s", decl.timeout_secs),
56 ),
57 }
58}