@@ -2,19 +2,11 @@ use clap::{builder::ArgPredicate, Parser};
22use colored:: Colorize ;
33use indicatif:: { HumanDuration , ProgressBar , ProgressDrawTarget , ProgressStyle } ;
44use miette:: { miette, Context , IntoDiagnostic } ;
5- use ssh2:: { ErrorCode , OpenFlags , OpenType , Session , Sftp } ;
65use std:: {
7- borrow:: Cow ,
8- collections:: HashMap ,
9- fmt, fs,
10- io:: BufWriter ,
11- net:: Ipv4Addr ,
12- path:: { Component , Path , PathBuf } ,
13- str:: FromStr ,
6+ borrow:: Cow , collections:: HashMap , fmt, fs, net:: Ipv4Addr , process:: Stdio , str:: FromStr ,
147 time:: Duration ,
158} ;
16- use tokio:: { self , net:: TcpStream } ;
17- use walkdir:: { DirEntry , WalkDir } ;
9+ use tokio:: { self , process:: Command } ;
1810use yggdrasil:: core:: config:: showtime:: ShowtimeConfig ;
1911use yggdrasil:: prelude:: * ;
2012
@@ -33,14 +25,8 @@ const ROBOT_TARGET: &str = "x86_64-unknown-linux-gnu";
3325const RELEASE_PATH_REMOTE : & str = "./target/x86_64-unknown-linux-gnu/release/yggdrasil" ;
3426const RELEASE_PATH_LOCAL : & str = "./target/release/yggdrasil" ;
3527const DEPLOY_PATH : & str = "./deploy/yggdrasil" ;
36- const CONNECTION_TIMEOUT : u64 = 5 ;
37- const LOCAL_ROBOT_ID_STR : & str = "0" ;
3828
39- /// The size of the `BufWriter`'s buffer.
40- ///
41- /// This is currently set to 1 MiB, as the [`Write`] implementation for [`ssh2::sftp::File`]
42- /// is rather slow due to the locking mechanism.
43- const UPLOAD_BUFFER_SIZE : usize = 1024 * 1024 ;
29+ const LOCAL_ROBOT_ID_STR : & str = "local" ;
4430
4531// enum for either the name or the number of a robot thats given
4632#[ derive( Clone , Debug ) ]
@@ -601,157 +587,28 @@ pub(crate) async fn stop_single_yggdrasil_service(robot: &Robot, output: Output)
601587}
602588
603589/// Copy the contents of the 'deploy' folder to the robot.
604- pub ( crate ) async fn upload_to_robot ( addr : & Ipv4Addr , output : Output ) -> Result < ( ) > {
605- output. connecting_phase ( addr) ;
606- let sftp = create_sftp_connection ( addr) . await ?;
607- match output. clone ( ) {
608- Output :: Silent => { }
609- Output :: Multi ( pb) => {
610- pb. set_message ( format ! ( "{}" , "Connected" . bright_blue( ) . bold( ) ) ) ;
611- }
612- Output :: Single ( pb) => {
613- pb. set_message ( format ! ( "{}" , " Connected" . bright_blue( ) . bold( ) ) ) ;
614- }
615- }
616-
617- let entries: Vec < DirEntry > = WalkDir :: new ( "./deploy" )
618- . into_iter ( )
619- . filter_map ( std:: result:: Result :: ok)
620- . filter ( |e| !e. file_name ( ) . to_string_lossy ( ) . starts_with ( '.' ) )
621- . collect ( ) ;
622- let num_files = entries
623- . iter ( )
624- . filter ( |e| e. metadata ( ) . unwrap ( ) . is_file ( ) )
625- . count ( ) ;
626-
627- output. upload_phase ( num_files as u64 ) ;
628-
629- for entry in & entries {
630- let remote_path = get_remote_path ( entry. path ( ) ) ;
631-
632- if entry. path ( ) . is_dir ( ) {
633- // Ensure all directories exist on remote
634- ensure_directory_exists ( & sftp, remote_path) ?;
635- continue ;
636- }
637-
638- let file_remote = sftp
639- . open_mode (
640- & remote_path,
641- OpenFlags :: WRITE | OpenFlags :: TRUNCATE ,
642- 0o777 ,
643- OpenType :: File ,
644- )
645- . map_err ( |e| Error :: Sftp {
646- source : e,
647- msg : format ! ( "Failed to open remote file {:?}!" , entry. path( ) ) ,
648- } ) ?;
649-
650- let mut file_local = std:: fs:: File :: open ( entry. path ( ) ) ?;
651-
652- match output. clone ( ) {
653- Output :: Silent => { }
654- Output :: Multi ( pb) => {
655- pb. set_message ( format ! ( "{}" , entry. path( ) . to_string_lossy( ) . dimmed( ) ) ) ;
656- }
657- Output :: Single ( pb) => {
658- pb. set_length ( file_local. metadata ( ) ?. len ( ) ) ;
659- pb. set_message ( format ! ( "{}" , entry. path( ) . to_string_lossy( ) ) ) ;
660- }
661- }
662-
663- // Since `file_remote` impl's Write, we can just copy directly using a BufWriter!
664- // The Write impl is rather slow, so we set a large buffer size of 1 mb.
665- let mut buf_writer = BufWriter :: with_capacity ( UPLOAD_BUFFER_SIZE , file_remote) ;
666-
667- match output. clone ( ) {
668- Output :: Silent => {
669- std:: io:: copy ( & mut file_local, & mut buf_writer) ?;
670- }
671- Output :: Multi ( pb) => {
672- std:: io:: copy ( & mut file_local, & mut buf_writer) ?;
673- pb. inc ( 1 ) ;
674- }
675- Output :: Single ( pb) => {
676- std:: io:: copy ( & mut file_local, & mut pb. wrap_write ( buf_writer) )
677- . map_err ( Error :: Io ) ?;
590+ pub ( crate ) async fn upload_to_robot ( addr : & Ipv4Addr ) -> Result < ( ) > {
591+ let output = Command :: new ( "rsync" )
592+ . args ( [
593+ "-az" ,
594+ "deploy/" ,
595+ & format ! ( "nao@{addr}:/home/nao" ) ,
596+ "--out-format=\" %f\" " ,
597+ ] )
598+ . stdout ( Stdio :: piped ( ) )
599+ . stderr ( Stdio :: piped ( ) )
600+ . output ( )
601+ . await ?;
678602
679- pb . println ( format ! (
680- "{} {}" ,
681- " Uploaded" . bright_blue ( ) . bold ( ) ,
682- entry . path ( ) . to_string_lossy ( ) . dimmed ( )
683- ) ) ;
684- }
603+ if !output . status . success ( ) {
604+ if let Some ( code ) = output . status . code ( ) {
605+ return Err ( Error :: Rsync {
606+ exit_code : code ,
607+ reason : String :: from_utf8_lossy ( & output . stderr ) . into_owned ( ) ,
608+ } ) ;
685609 }
686610 }
687611
688- output. spinner ( ) ;
689-
690- if let Output :: Multi ( pb) = & output {
691- pb. set_message ( format ! (
692- " {} {}" ,
693- "Uploaded" . green( ) . bold( ) ,
694- addr. to_string( ) . red( )
695- ) ) ;
696- }
697-
612+ println ! ( "{}" , String :: from_utf8_lossy( & output. stdout) ) ;
698613 Ok ( ( ) )
699614}
700-
701- async fn create_sftp_connection ( ip : & Ipv4Addr ) -> Result < Sftp > {
702- let tcp = tokio:: time:: timeout (
703- Duration :: from_secs ( CONNECTION_TIMEOUT ) ,
704- TcpStream :: connect ( format ! ( "{ip}:22" ) ) ,
705- )
706- . await
707- . map_err ( Error :: Elapsed ) ??;
708- let mut session = Session :: new ( ) . map_err ( |e| Error :: Sftp {
709- source : e,
710- msg : "Failed to create ssh session!" . to_owned ( ) ,
711- } ) ?;
712-
713- session. set_tcp_stream ( tcp) ;
714- session. handshake ( ) . map_err ( |e| Error :: Sftp {
715- source : e,
716- msg : "Failed to perform ssh handshake!" . to_owned ( ) ,
717- } ) ?;
718- session
719- . userauth_password ( "nao" , "" )
720- . map_err ( |e| Error :: Sftp {
721- source : e,
722- msg : "Failed to authenticate using ssh!" . to_owned ( ) ,
723- } ) ?;
724-
725- session. sftp ( ) . map_err ( |e| Error :: Sftp {
726- source : e,
727- msg : "Failed to create sftp session!" . to_owned ( ) ,
728- } )
729- }
730-
731- fn ensure_directory_exists ( sftp : & Sftp , remote_path : impl AsRef < Path > ) -> Result < ( ) > {
732- match sftp. mkdir ( remote_path. as_ref ( ) , 0o777 ) {
733- Ok ( ( ) ) => Ok ( ( ) ) ,
734- // Error code 4, means the directory already exists, so we can ignore it
735- Err ( error) if error. code ( ) == ErrorCode :: SFTP ( 4 ) => Ok ( ( ) ) ,
736- Err ( error) => Err ( Error :: Sftp {
737- source : error,
738- msg : "Failed to ensure directory exists" . to_owned ( ) ,
739- } ) ,
740- }
741- }
742-
743- fn get_remote_path ( local_path : & Path ) -> PathBuf {
744- let mut remote_path = PathBuf :: from ( "/home/nao" ) ;
745-
746- for component in local_path. components ( ) {
747- // Would be nice to replace this with an if let chain once https://github.com/rust-lang/rust/issues/53667#issuecomment-1374336460 is stable.
748- match component {
749- // Prevent "deploy" from being added to the remote path, as we'll deploy directly to home directory.
750- Component :: Normal ( c) if c != "deploy" => remote_path. push ( c) ,
751- // Any other component kind should ignored, such as ".".
752- _ => continue ,
753- }
754- }
755-
756- remote_path
757- }
0 commit comments