use super::Transport;
use std::process::Command;
pub struct SshTransport {
target: String,
config: Option<String>,
}
impl SshTransport {
pub fn new(target: &str) -> Self {
SshTransport {
target: target.to_string(),
config: std::env::var("FEZ_SSH_CONFIG")
.ok()
.filter(|s| !s.is_empty()),
}
}
}
impl Transport for SshTransport {
fn command(&self) -> Command {
let mut cmd = Command::new("ssh");
if let Some(cfg) = &self.config {
cmd.arg("-F").arg(cfg);
}
cmd.arg("-o")
.arg("BatchMode=yes")
.arg("-o")
.arg("ControlMaster=auto")
.arg("-o")
.arg("ControlPersist=60")
.arg("-o")
.arg("ControlPath=~/.ssh/fez-%r@%h:%p")
.arg(&self.target)
.arg("--")
.arg("cockpit-bridge");
cmd
}
fn host_label(&self) -> String {
self.target.clone()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn builds_ssh_argv() {
let t = SshTransport::new("fedora@host.example");
let cmd = t.command();
assert_eq!(cmd.get_program(), "ssh");
let args: Vec<String> = cmd
.get_args()
.map(|a| a.to_string_lossy().into_owned())
.collect();
assert!(args.windows(2).any(|w| w == ["-o", "BatchMode=yes"]));
assert!(args.contains(&"fedora@host.example".to_string()));
let dd = args.iter().position(|a| a == "--").unwrap();
assert_eq!(args[dd + 1], "cockpit-bridge");
}
#[test]
fn host_label_is_target() {
assert_eq!(SshTransport::new("h1").host_label(), "h1");
}
#[test]
fn injects_config_flag_when_set() {
let t = SshTransport {
target: "target".into(),
config: Some("/run/fez/ssh_config".into()),
};
let args: Vec<String> = t
.command()
.get_args()
.map(|a| a.to_string_lossy().into_owned())
.collect();
assert!(
args.windows(2).any(|w| w == ["-F", "/run/fez/ssh_config"]),
"expected -F /run/fez/ssh_config, got {args:?}"
);
}
#[test]
fn omits_config_flag_when_unset() {
let t = SshTransport {
target: "target".into(),
config: None,
};
let args: Vec<String> = t
.command()
.get_args()
.map(|a| a.to_string_lossy().into_owned())
.collect();
assert!(!args.iter().any(|a| a == "-F"), "unexpected -F in {args:?}");
}
}