use std::path::Path;
use std::time::{Duration, Instant};
#[cfg(unix)]
pub fn write_executable(path: &Path, script: &str) {
use std::os::unix::fs::PermissionsExt;
std::fs::write(path, script).unwrap();
std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o755)).unwrap();
let mut attempts = 0;
loop {
match std::process::Command::new(path).arg("warmup").output() {
Err(e) if e.kind() == std::io::ErrorKind::ExecutableFileBusy && attempts < 200 => {
attempts += 1;
std::thread::sleep(Duration::from_millis(5));
}
Err(e) => panic!("fake CLI {} did not run: {e}", path.display()),
Ok(_) => return,
}
}
}
#[cfg(unix)]
pub fn fake_cli(name: &str, body: &str) -> (tempfile::TempDir, String) {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join(name);
write_executable(&path, &format!("#!/bin/sh\n{body}"));
let cli = path.display().to_string();
(dir, cli)
}
pub fn wait_until(timeout: Duration, mut cond: impl FnMut() -> bool) -> bool {
let deadline = Instant::now() + timeout;
loop {
if cond() {
return true;
}
if Instant::now() >= deadline {
return false;
}
std::thread::sleep(Duration::from_millis(20));
}
}
pub fn read_pidfile(pidfile: &Path) -> u32 {
assert!(
wait_until(Duration::from_secs(5), || std::fs::read_to_string(pidfile)
.is_ok_and(|s| s.trim().parse::<u32>().is_ok())),
"the fake CLI never wrote a pid to {}",
pidfile.display()
);
std::fs::read_to_string(pidfile)
.unwrap()
.trim()
.parse()
.unwrap()
}
pub fn pid_gone(pid: u32) -> bool {
wait_until(Duration::from_secs(5), || {
!crate::container_net::pid_alive(pid)
})
}
#[cfg(unix)]
pub fn kill_pid(pid: u32) {
let _ = unsafe { libc::kill(pid as libc::pid_t, libc::SIGKILL) };
}