@@ -3,7 +3,8 @@ use bollard::{
3
3
models:: { Port , PortTypeEnum } ,
4
4
Docker ,
5
5
} ;
6
- use openssh:: { ForwardType , KnownHosts , SessionBuilder } ;
6
+
7
+ use openssh:: { ForwardType , KnownHosts , Session , SessionBuilder } ;
7
8
use regex:: Regex ;
8
9
use std:: {
9
10
collections:: HashSet ,
@@ -19,35 +20,16 @@ use std::{
19
20
20
21
use crate :: { model:: types:: AnyError , util:: labels} ;
21
22
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 > {
41
28
if local_socket_path. exists ( ) {
42
29
fs:: remove_file ( local_socket_path) ?;
43
30
}
44
31
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 ?;
51
33
52
34
println ! ( "SSH: connected to {}" , & ssh_url) ;
53
35
@@ -86,11 +68,56 @@ pub async fn remote(ssh_url: &str, local_docker_host: &str) -> Result<(), AnyErr
86
68
"Run 'export DOCKER_HOST=unix://{}' to make the socket useful for local tools" ,
87
69
local_socket_path. display( )
88
70
) ;
71
+ Ok ( session)
72
+ }
89
73
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 ?;
90
100
let docker = Docker :: connect_with_local_defaults ( ) ?. with_timeout ( Duration :: from_secs ( 10 ) ) ;
91
101
let mut tunnels = HashSet :: < u16 > :: new ( ) ;
92
102
93
103
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
+
94
121
let containers = match docker
95
122
. list_containers ( Some ( ListContainersOptions {
96
123
filters : ( & labels:: Labels :: default ( ) ) . into ( ) ,
@@ -133,7 +160,6 @@ pub async fn remote(ssh_url: &str, local_docker_host: &str) -> Result<(), AnyErr
133
160
134
161
let listen_socket = format ! ( "127.0.0.1:{}" , private_port) ;
135
162
let connect_socket = format ! ( "127.0.0.1:{}" , public_port) ;
136
- session. check ( ) . await ?;
137
163
138
164
if !tunnels. contains ( & public_port) {
139
165
if is_available ( & private_port) {
@@ -163,6 +189,7 @@ pub async fn remote(ssh_url: &str, local_docker_host: &str) -> Result<(), AnyErr
163
189
break ;
164
190
}
165
191
}
192
+ //TODO: store and close port forwards here
166
193
session. close ( ) . await ?;
167
194
std:: process:: exit ( 0 ) ;
168
195
}
0 commit comments