@@ -7,13 +7,15 @@ extern crate ssh2;
7
7
8
8
use bzip2:: read:: BzDecoder ;
9
9
use clap:: { Arg , Command as App } ;
10
+ use env_logger:: Env ;
11
+ use log:: { error, warn, info, debug} ;
10
12
use regex:: Regex ;
11
13
use serde:: { Deserialize , Serialize } ;
12
14
use ssh2:: Session ;
13
15
use std:: collections:: HashMap ;
14
16
use std:: error:: Error ;
15
17
use std:: fs:: { self , File } ;
16
- use std:: io:: { copy, BufReader , BufWriter , Read } ;
18
+ use std:: io:: { copy, BufReader , BufWriter , Read , Write } ;
17
19
use std:: net:: TcpStream ;
18
20
use std:: path:: Path ;
19
21
use std:: process;
@@ -44,13 +46,12 @@ impl Config {
44
46
let ssh_host = cap
45
47
. get ( 2 )
46
48
. unwrap_or_else ( || {
47
- eprintln ! ( "Failed to parse ssh host" ) ;
49
+ error ! ( "Failed to parse ssh host" ) ;
48
50
process:: exit ( 1 ) ;
49
51
} )
50
52
. as_str ( ) ;
51
53
52
54
let mut config: Config = match config_file. exists ( ) {
53
- // let config_path = ;
54
55
true => Config :: parse ( config_file. to_str ( ) . unwrap ( ) , ssh_host) ,
55
56
false => Config {
56
57
ssh_host : ssh_host. to_string ( ) ,
@@ -62,7 +63,7 @@ impl Config {
62
63
match cap. get ( 1 ) {
63
64
Some ( val) => config. ssh_user = val. as_str ( ) . to_string ( ) ,
64
65
None => {
65
- eprintln ! ( "SSH username cannot be empty" ) ;
66
+ error ! ( "SSH username cannot be empty" ) ;
66
67
process:: exit ( 1 ) ;
67
68
}
68
69
}
@@ -78,7 +79,7 @@ impl Config {
78
79
match Config :: parse_config_file ( path, host) {
79
80
Ok ( config) => config,
80
81
Err ( err) => {
81
- eprintln ! ( "Failed to parse config file - {}" , err) ;
82
+ error ! ( "Failed to parse config file - {}" , err) ;
82
83
Config {
83
84
ssh_host : host. to_string ( ) ,
84
85
..Default :: default ( )
@@ -100,6 +101,13 @@ impl Config {
100
101
}
101
102
102
103
fn main ( ) -> Result < ( ) , Box < dyn Error > > {
104
+ // Initialize the logger with the default environment variable "RUST_LOG"
105
+ env_logger:: Builder :: from_env ( Env :: default ( ) . default_filter_or ( "info" ) )
106
+ . format ( |buf, record| {
107
+ writeln ! ( buf, "[{}] {}" , record. level( ) , record. args( ) )
108
+ } )
109
+ . init ( ) ;
110
+
103
111
let matches = App :: new ( "Database Importer" )
104
112
. about ( "Import database from remote server" )
105
113
. arg (
@@ -156,7 +164,7 @@ fn main() -> Result<(), Box<dyn Error>> {
156
164
let host = match matches. value_of ( "host" ) {
157
165
Some ( n) => n,
158
166
None => {
159
- eprintln ! ( "Target host cannot be empty" ) ;
167
+ error ! ( "Target host cannot be empty" ) ;
160
168
process:: exit ( 1 ) ;
161
169
}
162
170
} ;
@@ -166,7 +174,7 @@ fn main() -> Result<(), Box<dyn Error>> {
166
174
let tables = match matches. values_of ( "table" ) {
167
175
Some ( n) => n,
168
176
None => {
169
- eprintln ! ( "Target tables cannot be empty" ) ;
177
+ error ! ( "Target tables cannot be empty" ) ;
170
178
process:: exit ( 1 ) ;
171
179
}
172
180
} ;
@@ -182,7 +190,7 @@ fn main() -> Result<(), Box<dyn Error>> {
182
190
None => {
183
191
// User option was not specified.
184
192
// Use ssh username, if available
185
- eprintln ! ( "Using SSH username as database username" ) ;
193
+ info ! ( "Using SSH username as database username" ) ;
186
194
config. ssh_user . to_owned ( )
187
195
}
188
196
} ,
@@ -195,18 +203,18 @@ fn main() -> Result<(), Box<dyn Error>> {
195
203
None => {
196
204
// User option was not specified.
197
205
// Use ssh username, if available
198
- eprintln ! ( "Inferring database name as {}_db" , db_user) ;
206
+ info ! ( "Inferring database name as {}_db" , db_user) ;
199
207
format ! ( "{}_db" , db_user)
200
208
}
201
209
} ,
202
210
} ;
203
211
204
- let db_pass = match matches. value_of ( "db_pass " ) {
212
+ let db_pass = match matches. value_of ( "dbpass " ) {
205
213
Some ( val) => val. to_owned ( ) ,
206
214
None => match config. db_pass {
207
215
Some ( val) => val,
208
216
None => {
209
- eprintln ! ( "Database password cannot be empty" ) ;
217
+ error ! ( "Database password cannot be empty" ) ;
210
218
process:: exit ( 2 ) ;
211
219
}
212
220
} ,
@@ -217,7 +225,7 @@ fn main() -> Result<(), Box<dyn Error>> {
217
225
// Connect to the remote server
218
226
let ssh_host_port = format ! ( "{}:{}" , config. ssh_host, config. ssh_port) ;
219
227
let tcp = TcpStream :: connect ( & ssh_host_port) . unwrap_or_else ( |err| {
220
- eprintln ! ( "{}" , err) ;
228
+ error ! ( "{}" , err) ;
221
229
process:: exit ( 1 ) ;
222
230
} ) ;
223
231
@@ -226,24 +234,21 @@ fn main() -> Result<(), Box<dyn Error>> {
226
234
sess. handshake ( ) . expect ( "SSH handshake failed" ) ;
227
235
228
236
// Try to authenticate with the first identity in the agent.
229
- eprint ! ( "Attept to authenticate with ssh-agent..." ) ;
237
+ info ! ( "Attempt to authenticate with ssh-agent..." ) ;
230
238
let _ = sess. userauth_agent ( & config. ssh_user ) ;
231
239
232
240
// Make sure we succeeded
233
241
if !sess. authenticated ( ) {
234
- eprintln ! ( "FAILED" ) ;
235
-
236
- eprintln ! ( "Falling back to password login" ) ;
237
- eprint ! ( "Enter password: " ) ;
238
-
242
+ warn ! ( "SSH-agent authentication failed. Falling back to password login." ) ;
243
+ info ! ( "Enter password: " ) ;
239
244
let ssh_pass = rpassword:: read_password ( ) . unwrap ( ) ;
240
245
sess. userauth_password ( & config. ssh_user , & ssh_pass)
241
246
. unwrap_or_else ( |_| {
242
- eprintln ! ( "Failed to authenticate to remote server" ) ;
247
+ error ! ( "Failed to authenticate to remote server" ) ;
243
248
process:: exit ( 1 ) ;
244
249
} ) ;
245
250
} else {
246
- eprintln ! ( "OK " ) ;
251
+ info ! ( "SSH-agent authentication succeeded. " ) ;
247
252
}
248
253
249
254
let mut remote_temp_file = String :: new ( ) ;
@@ -254,13 +259,6 @@ fn main() -> Result<(), Box<dyn Error>> {
254
259
channel. read_to_string ( & mut remote_temp_file) . unwrap ( ) ;
255
260
let remote_temp_file = remote_temp_file. trim ( ) ;
256
261
257
- // ctrlc::set_handler(move || {
258
- // // Handle early termination
259
- // let status = sess.sftp().and_then(|sftp| {
260
- // sftp.unlink(Path::new(remote_temp_file))
261
- // });
262
- // }).expect("Error setting Ctrl-C handler");
263
-
264
262
let pass_arg = format ! ( "-p{}" , & db_pass) ;
265
263
let mut v = vec ! [ "mysqldump" , "-u" , & db_user, & pass_arg, & db_name] ;
266
264
@@ -279,33 +277,29 @@ fn main() -> Result<(), Box<dyn Error>> {
279
277
remote_temp_file
280
278
) ;
281
279
282
- eprint ! ( "Exporting database on target server..." ) ;
280
+ info ! ( "Exporting database on target server..." ) ;
283
281
let mut channel = sess. channel_session ( ) . ok ( ) . unwrap ( ) ;
284
282
let exit_status = channel
285
283
. exec ( & arg)
286
284
. and_then ( |_| channel. close ( ) )
287
- // .and_then(|_| channel.wait_close())
288
285
. and_then ( |_| channel. exit_status ( ) ) ;
289
286
290
287
match exit_status {
291
- Ok ( 0 ) => ( ) ,
288
+ Ok ( 0 ) => info ! ( "Database export succeeded." ) ,
292
289
_ => {
293
- eprintln ! ( "ERR" ) ;
294
- eprintln ! ( "Failed to export database" ) ;
290
+ error ! ( "Failed to export database" ) ;
295
291
process:: exit ( 4 ) ;
296
292
}
297
293
}
298
294
299
- eprintln ! ( "OK" ) ;
300
-
301
295
let ( remote_file, stat) = sess
302
296
. scp_recv ( Path :: new ( & remote_temp_file) )
303
297
. unwrap_or_else ( |err| {
304
- eprintln ! ( "Failed to download file - {}" , err) ;
298
+ error ! ( "Failed to download file - {}" , err) ;
305
299
process:: exit ( 2 ) ;
306
300
} ) ;
307
301
308
- eprintln ! ( "Exported file size: {}" , stat. size( ) ) ;
302
+ info ! ( "Exported file size: {}" , stat. size( ) ) ;
309
303
310
304
let temp_file = Builder :: new ( )
311
305
. suffix ( ".sql.bz2" )
@@ -325,13 +319,13 @@ fn main() -> Result<(), Box<dyn Error>> {
325
319
match copy ( & mut remote_file, & mut target) {
326
320
Ok ( _) => ( ) ,
327
321
Err ( err) => {
328
- eprintln ! ( "Failed to download exported database dump - {}" , err) ;
322
+ error ! ( "Failed to download exported database dump - {}" , err) ;
329
323
process:: exit ( 3 ) ;
330
324
}
331
325
}
326
+ debug ! ( "Database dump downloaded to {:?}" , path) ;
332
327
333
328
progressbar. finish_and_clear ( ) ;
334
- println ! ( "Downloading database dump...OK" ) ;
335
329
336
330
let mut channel = sess. channel_session ( ) . ok ( ) . unwrap ( ) ;
337
331
let arg = format ! ( "rm -f {}" , remote_temp_file) ;
@@ -341,8 +335,8 @@ fn main() -> Result<(), Box<dyn Error>> {
341
335
. and_then ( |_| channel. exit_status ( ) ) ;
342
336
343
337
match exit_status {
344
- Ok ( 0 ) => eprintln ! ( "Removed temporary file from remote filesystem...OK " ) ,
345
- _ => eprintln ! ( "Failed to delete temporary file from remote filesystem" ) ,
338
+ Ok ( 0 ) => info ! ( "Removed temporary file from remote filesystem." ) ,
339
+ _ => warn ! ( "Failed to delete temporary file from remote filesystem. " ) ,
346
340
}
347
341
348
342
// TODO: Delete temporary file from remote filesystem
@@ -356,36 +350,40 @@ fn main() -> Result<(), Box<dyn Error>> {
356
350
. spawn ( )
357
351
. expect ( "Failed to spawn mysql client" ) ;
358
352
353
+ debug ! ( "Spawning mysql client" ) ;
359
354
if let Some ( stdin) = & mut cmd. stdin {
360
355
let mut writer = BufWriter :: new ( stdin) ;
361
356
357
+ debug ! ( "Copying uncompressed dump to mysql client stdin" ) ;
362
358
// Decompress and import to local mysql database
363
359
match copy ( & mut Box :: new ( BzDecoder :: new ( f) ) , & mut writer) {
364
- Ok ( _) => {
365
- eprintln ! ( "Import completed successfully" )
366
- }
360
+ Ok ( _) => info ! ( "Database import completed successfully." ) ,
367
361
Err ( err) => {
368
- eprintln ! ( "Failed to import database dump - {}" , err) ;
362
+ error ! ( "Failed to import database dump - {}" , err) ;
369
363
process:: exit ( 5 ) ;
370
364
}
371
365
} ;
372
366
}
373
367
368
+ debug ! ( "Waiting for import to complete" ) ;
369
+
374
370
let result = match cmd. try_wait ( ) {
375
371
Ok ( Some ( status) ) => status. code ( ) ,
376
372
Ok ( None ) => cmd. wait ( ) . unwrap ( ) . code ( ) ,
377
373
Err ( e) => {
378
- eprintln ! ( "error attempting to wait: {}" , e) ;
374
+ error ! ( "Error attempting to wait: {}" , e) ;
379
375
process:: exit ( 5 ) ;
380
376
}
381
377
} ;
382
378
379
+ debug ! ( "Removing temporary file {:?}" , path) ;
383
380
fs:: remove_file ( path) ?;
384
381
385
382
match result {
386
383
Some ( 0 ) => ( ) ,
387
- n => eprintln ! ( "Import failed with exit code {:?}" , n) ,
384
+ n => error ! ( "Import failed with exit code {:?}" , n) ,
388
385
} ;
389
386
390
387
Ok ( ( ) )
391
388
}
389
+
0 commit comments