Skip to content

Commit 88772a1

Browse files
committed
fix: try re-connect to remote if connection lost + deps
1 parent bd00582 commit 88772a1

File tree

3 files changed

+86
-59
lines changed

3 files changed

+86
-59
lines changed

Cargo.lock

+31-31
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "rooz"
3-
version = "0.101.0"
3+
version = "0.102.0"
44
edition = "2021"
55

66
[dependencies]

src/cmd/remote.rs

+54-27
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ use bollard::{
33
models::{Port, PortTypeEnum},
44
Docker,
55
};
6-
use openssh::{ForwardType, KnownHosts, SessionBuilder};
6+
7+
use openssh::{ForwardType, KnownHosts, Session, SessionBuilder};
78
use regex::Regex;
89
use std::{
910
collections::HashSet,
@@ -19,35 +20,16 @@ use std::{
1920

2021
use crate::{model::types::AnyError, util::labels};
2122

22-
pub async fn remote(ssh_url: &str, local_docker_host: &str) -> Result<(), AnyError> {
23-
let (sender, receiver) = mpsc::channel::<()>();
24-
25-
let tx_mutex = Mutex::<Option<Sender<()>>>::new(Some(sender));
26-
27-
ctrlc::set_handler(move || {
28-
if let Some(tx) = tx_mutex.lock().unwrap().take() {
29-
tx.send(()).unwrap();
30-
}
31-
})?;
32-
33-
let re = Regex::new(r"^unix://").unwrap();
34-
let expanded_socket = shellexpand::tilde(&re.replace(&local_docker_host, "")).into_owned();
35-
let local_socket_path = Path::new(&expanded_socket);
36-
37-
if let Some(path) = local_socket_path.parent() {
38-
fs::create_dir_all(path)?;
39-
}
40-
23+
async fn connect(
24+
builder: &SessionBuilder,
25+
ssh_url: &str,
26+
local_socket_path: &Path,
27+
) -> Result<Session, AnyError> {
4128
if local_socket_path.exists() {
4229
fs::remove_file(local_socket_path)?;
4330
}
4431

45-
let session = SessionBuilder::default()
46-
.known_hosts_check(KnownHosts::Accept)
47-
.connect_timeout(Duration::from_secs(5))
48-
.server_alive_interval(Duration::from_secs(60))
49-
.connect_mux(&ssh_url)
50-
.await?;
32+
let session = builder.connect_mux(&ssh_url).await?;
5133

5234
println!("SSH: connected to {}", &ssh_url);
5335

@@ -86,11 +68,56 @@ pub async fn remote(ssh_url: &str, local_docker_host: &str) -> Result<(), AnyErr
8668
"Run 'export DOCKER_HOST=unix://{}' to make the socket useful for local tools",
8769
local_socket_path.display()
8870
);
71+
Ok(session)
72+
}
8973

74+
pub async fn remote(ssh_url: &str, local_docker_host: &str) -> Result<(), AnyError> {
75+
let (sender, receiver) = mpsc::channel::<()>();
76+
77+
let tx_mutex = Mutex::<Option<Sender<()>>>::new(Some(sender));
78+
79+
ctrlc::set_handler(move || {
80+
if let Some(tx) = tx_mutex.lock().unwrap().take() {
81+
tx.send(()).unwrap();
82+
}
83+
})?;
84+
85+
let re = Regex::new(r"^unix://").unwrap();
86+
let expanded_socket = shellexpand::tilde(&re.replace(&local_docker_host, "")).into_owned();
87+
let local_socket_path = Path::new(&expanded_socket);
88+
89+
if let Some(path) = local_socket_path.parent() {
90+
fs::create_dir_all(path)?;
91+
}
92+
93+
let mut builder = SessionBuilder::default();
94+
builder
95+
.known_hosts_check(KnownHosts::Strict)
96+
.connect_timeout(Duration::from_secs(5))
97+
.server_alive_interval(Duration::from_secs(5));
98+
99+
let mut session = connect(&builder, ssh_url, local_socket_path).await?;
90100
let docker = Docker::connect_with_local_defaults()?.with_timeout(Duration::from_secs(10));
91101
let mut tunnels = HashSet::<u16>::new();
92102

93103
loop {
104+
match session.check().await {
105+
Ok(_) => (),
106+
Err(_) => {
107+
eprintln!("SSH connection lost. Reconnecting...");
108+
match connect(&builder, ssh_url, local_socket_path).await {
109+
Ok(s) => session = s,
110+
Err(error) => {
111+
eprintln!("ERROR: {}", error);
112+
if let Some(()) = receiver.recv_timeout(Duration::from_secs(3)).ok() {
113+
break;
114+
}
115+
continue;
116+
}
117+
}
118+
}
119+
}
120+
94121
let containers = match docker
95122
.list_containers(Some(ListContainersOptions {
96123
filters: (&labels::Labels::default()).into(),
@@ -133,7 +160,6 @@ pub async fn remote(ssh_url: &str, local_docker_host: &str) -> Result<(), AnyErr
133160

134161
let listen_socket = format!("127.0.0.1:{}", private_port);
135162
let connect_socket = format!("127.0.0.1:{}", public_port);
136-
session.check().await?;
137163

138164
if !tunnels.contains(&public_port) {
139165
if is_available(&private_port) {
@@ -163,6 +189,7 @@ pub async fn remote(ssh_url: &str, local_docker_host: &str) -> Result<(), AnyErr
163189
break;
164190
}
165191
}
192+
//TODO: store and close port forwards here
166193
session.close().await?;
167194
std::process::exit(0);
168195
}

0 commit comments

Comments
 (0)