use std::io::{BufRead, BufReader};
use std::process::{Command, Stdio};
use crate::config::{Result, TestbedError, VmProfile};
use crate::ssh::VmSession;
pub fn exec_streaming(profile: &VmProfile, cmd: &str) -> Result<i32> {
let wrapped = format!("nu -c {cmd:?}");
let mut ssh_cmd = Command::new("ssh");
ssh_cmd
.args([
"-o", "StrictHostKeyChecking=no",
"-o", "UserKnownHostsFile=/dev/null",
"-o", "LogLevel=ERROR",
"-p", &profile.ssh_port.to_string(),
"-i", "vagrant", ])
.arg(format!("{}@127.0.0.1", profile.user))
.arg(&wrapped)
.stdin(Stdio::null());
let mut child = ssh_cmd.spawn().map_err(|e| TestbedError::SshFailed {
port: profile.ssh_port,
source: anyhow::anyhow!("spawning ssh: {e}"),
})?;
let status = child.wait().map_err(|e| TestbedError::SshFailed {
port: profile.ssh_port,
source: anyhow::anyhow!("waiting for ssh: {e}"),
})?;
Ok(status.code().unwrap_or(1))
}
pub fn exec_streaming_session(session: &VmSession, cmd: &str) -> Result<i32> {
let key_path = find_ssh_key().ok_or_else(|| TestbedError::SshFailed {
port: session.port,
source: anyhow::anyhow!("no SSH key found for streaming exec"),
})?;
let mut ssh_cmd = Command::new("ssh");
ssh_cmd
.args([
"-o", "StrictHostKeyChecking=no",
"-o", "UserKnownHostsFile=/dev/null",
"-o", "LogLevel=ERROR",
"-o", "BatchMode=yes",
"-p", &session.port.to_string(),
"-i", &key_path,
])
.arg(format!("{}@127.0.0.1", session.user))
.arg(cmd)
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::piped());
let mut child = ssh_cmd.spawn().map_err(|e| TestbedError::SshFailed {
port: session.port,
source: anyhow::anyhow!("spawning ssh: {e}"),
})?;
let stdout = child.stdout.take().expect("stdout piped");
let stderr = child.stderr.take().expect("stderr piped");
let stdout_reader = BufReader::new(stdout);
let stderr_reader = BufReader::new(stderr);
std::thread::spawn(move || {
for line in stdout_reader.lines() {
if let Ok(line) = line {
println!("{line}");
}
}
});
std::thread::spawn(move || {
for line in stderr_reader.lines() {
if let Ok(line) = line {
eprintln!("{line}");
}
}
});
let status = child.wait().map_err(|e| TestbedError::SshFailed {
port: session.port,
source: anyhow::anyhow!("waiting for ssh: {e}"),
})?;
Ok(status.code().unwrap_or(1))
}
fn find_ssh_key() -> Option<String> {
if let Some(config_dir) = dirs::config_dir() {
let vagrant_key = config_dir.join("foundation_testbed/vagrant_insecure_key");
if vagrant_key.exists() {
return Some(vagrant_key.to_string_lossy().to_string());
}
}
let key_names = ["id_ed25519", "id_rsa", "id_ecdsa"];
if let Some(home) = dirs::home_dir() {
let ssh_dir = home.join(".ssh");
for key_name in &key_names {
let key_path = ssh_dir.join(key_name);
if key_path.exists() {
return Some(key_path.to_string_lossy().to_string());
}
}
}
None
}