|
|
@@ -13,11 +13,29 @@ use crate::config::{Config, Node};
|
|
|
|
|
|
const CONNECT_TIMEOUT: Duration = Duration::from_secs(5);
|
|
|
|
|
|
-/// Establishes an SSH session to a node using the config credentials.
|
|
|
+/// Establishes an SSH session to a node using the config credentials, with a 5s connect
|
|
|
+/// and inactivity timeout. Use [`ssh_session_follow`] for long-lived streaming commands.
|
|
|
pub async fn ssh_session(config: &Config, node: &Node) -> Result<client::Handle<VerifyingHandler>> {
|
|
|
+ ssh_session_with_timeout(config, node, Some(CONNECT_TIMEOUT)).await
|
|
|
+}
|
|
|
+
|
|
|
+/// Like [`ssh_session`] but with no inactivity timeout — suitable for long-lived
|
|
|
+/// streaming commands such as `docker logs -f`.
|
|
|
+pub async fn ssh_session_follow(
|
|
|
+ config: &Config,
|
|
|
+ node: &Node,
|
|
|
+) -> Result<client::Handle<VerifyingHandler>> {
|
|
|
+ ssh_session_with_timeout(config, node, None).await
|
|
|
+}
|
|
|
+
|
|
|
+async fn ssh_session_with_timeout(
|
|
|
+ config: &Config,
|
|
|
+ node: &Node,
|
|
|
+ inactivity_timeout: Option<Duration>,
|
|
|
+) -> Result<client::Handle<VerifyingHandler>> {
|
|
|
let key = load_secret_key(&config.ssh_key_path)?;
|
|
|
let ssh_config = russh::client::Config {
|
|
|
- inactivity_timeout: Some(CONNECT_TIMEOUT),
|
|
|
+ inactivity_timeout,
|
|
|
..Default::default()
|
|
|
};
|
|
|
let expected_key = Arc::new(std::sync::Mutex::new(node.host_key.clone()));
|
|
|
@@ -131,6 +149,7 @@ pub async fn run_ssh_with_retry(
|
|
|
}
|
|
|
|
|
|
/// Runs a command via SSH and streams output to stdout (for follow mode).
|
|
|
+/// Uses a session with no inactivity timeout so long-lived streams are not killed.
|
|
|
pub async fn run_ssh_follow(
|
|
|
config: &Config,
|
|
|
node: &Node,
|
|
|
@@ -142,7 +161,7 @@ pub async fn run_ssh_follow(
|
|
|
return Ok(());
|
|
|
}
|
|
|
|
|
|
- let session = ssh_session(config, node).await?;
|
|
|
+ let session = ssh_session_follow(config, node).await?;
|
|
|
let mut channel = session.channel_open_session().await?;
|
|
|
channel.exec(true, command.as_bytes()).await?;
|
|
|
|