From fe6efaa4e44ff9f048622eb575f19e181eddb04e Mon Sep 17 00:00:00 2001 From: Ericson Soares Date: Fri, 2 Aug 2024 18:53:29 -0300 Subject: [PATCH] Setup client for Cloud Services in Node --- Cargo.lock | Bin 303613 -> 312594 bytes Cargo.toml | 4 + core/Cargo.toml | 2 + core/crates/cloud-services/Cargo.toml | 28 ++++ core/crates/cloud-services/src/error.rs | 21 +++ core/crates/cloud-services/src/lib.rs | 203 ++++++++++++++++++++++++ core/src/lib.rs | 26 +++ crates/ai/Cargo.toml | 2 +- 8 files changed, 285 insertions(+), 1 deletion(-) create mode 100644 core/crates/cloud-services/Cargo.toml create mode 100644 core/crates/cloud-services/src/error.rs create mode 100644 core/crates/cloud-services/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 8db081fa4a921b8b83f7828afa1347aa5ae1669f..ee6c3188e7a14c5918a4975acfdcf636fef3f886 100644 GIT binary patch delta 7650 zcmbtZd2n4l$WJ!Kc za&TBmhGp2Yaf43-Q<^Z7&w)V(vS$w}v`>OKcfyKMCYE9$n*%?+%jG0O9xT2)c zECdyZvn~`Su$&Msbm_{X)P?bucw5>K3TcAlPSad_rkx{s+&$FTw93tIJ+E(ctDQF2 z1{R;+m#yocGwt?uvn@Y1W!h7tWL(Dno6=2-`QTTU#cW45Qa!(+cX43jc>{|tjy*E4 z9G{?coK~mnjf>B1-!@e3{?oP9n(;l8!sR7lUIe3L$s8>t$7;*c7oO!sZoPGi`@l+q z75RWa%fdNd8iM}|7yGA@^^2>n{BUnZ$9eT+@9s%UOCf_6H1|c$SnhHom1iu7oI1sb z5{fx%B*m?zCXQ%Rmco_FQ}3Or`X3*Pdmqbst0&40i#P1OSf;jp_}?851qrq~8mmZT^>!;_Uv zYlhBXMPR6PVG3&~;VLNSnNY+y>O*b?lRj4^lO)owCPUQ|KOKojZ_m!CUb<^b+;uQ{ z>*D9{x@MrdWqP>Ua^Fzg(vz%+m+#EBRhM;^FJAKXl}UAZr>C*k&c3U9;NxbJDq1ir zO0G@8f-8(FB&AY1Cmd>NI8oAC>MiF=(wr&7bf8*dEuc4)dI&|ak>eivT`Pseg z;96lG*N*45vyruJGu<|fbQ`%HT0Xe>xMOEVv6f|hulVqt1Bsci+AB0#ZhVqfWE%}yVpoXDkn zch?cO@61^BrN3LTxc9zIz44-UHdtM>tAFtm4-EDXja@etTH&PbpyMo>knNsS%q#S< z6BIR)!5WJ$#y=Ksa8Fg?WS*Cr3bdvdI=Dc+@wCht#e=Zw23c>@Wc8UJZ;tMnY}fg+ zUY?p@jF=^3+O-Yl(`Goc;fIjdV#mYQ3My)+U#YHY5oEfu56D%2SuB?a2aYwJW= z8jDtlJBnl|j;w90jQ8J_Y{4y7#<}liL(9mCdrYw0YpPxF^*oo3=2m)XIDo?{#Q=D@ zXIKZL%n3ZCwZUVk93GZ2Pvf0;CBvx6S@FT!v$Gbz`;*V5z4h>n%{Kj_dAM(ILk(US z&hGZLN~d(L@e&7|UH8-*s@r}!n5gQ=gS#gJfVD)Wq>~Keqi|a}#KDYY*MBeuHb?BTfYuhV5&KCWZ`>U7=fN4JLQ>E_&g(~Y@>*1=FOgpU`m zT8J1Y)y&hw@z3u_&#dlx>PL;byBBAk-k%Km1$S~Ek#P}k*_`c}tOrK0vMeyph67@4 z$gy4*0I?+Joa>qm3L>mF*c7fD=z}Vw4QGzX>e#(QtHaE~{xNVsbH+?JuMd&#NiSG@ z?3qs_%j$)qJo~nq`l+wh74pM(g!dhUByOj29Y((sbh zlY}-i(o&;cZZX0J7;d;$GWQmc%`hE8=A0Ei4@4Fv(4jkC)yO;7R_{K#GM@Q-GO_r9 zUp>(~6b`)#f5^DP{vEh)_k_c&Nw0OuL|zEz9o3}=c%bH1=YfMnOvt5jz!Rtrjok=^ z&b^|6nNU6S!J*#fj1M#Op%vfp56Rh!?xkmwu4Gs|@Vlcu@rp*WCSG`NvS$+I6qE^J zbIcNEnFg7Qk^&Vh2v;aw;@@0j-W8}BOawT=MteggJ3UT)IvKG#$=8b5eNvTkW9B`I|%b0~*0hhWg9 zM8|=bp{)v3vQWM-1cWWQ#f0F~L2*!(bCyapLYI`he`aW zOOm%uZ~^$k!%?umR?hfB@KWTZhhmYC9s&zd-g^PT2EvxU02~(%GQ?6=RtHuLRjcpm zjX$_8(VfA=$&uxaZl!N^iJ5eP0e%Ut2{3e67uJeaKS-2Yce|H}2P*{LyW+OGx|R(<&$dnQW`2?nGO-cTTUfp!d9qktuZ zL@kN}?q7H#oT8<4-h-_)0e@1)xL|R|Rmt#{V0?E5&CH>x+3UK$x+P)hLcC)+YTEapnPOR;nn{CfK z)ADi0Gs$4Q5DXlLj-+e)L#s90>VBi$o*KJ09E!I-kqq|EPw#ccO@-=Re7vW7t?Hqv zb%}__E=+b$0(7jYADckOk`g@u?c}|zG1Qs7?vH@pP~2eTt#AcX8UPFxTM({!Jm#|D z>XzmOapIosnnDR11n}Epwt6Gn6XOvgfpV=*8i9bO9RlYmcUvc8>l{(z7uh zJTF<>*Pd(60M#W>zW)cw#?I5t9T6y|m9ZuWruFkNM`+@ug#_ne?s@(3}Ni zGSd+KPy?JmvpNf6_taSkMkt<8);0kpb@wCWPS0!u1VK4 zrki$u^`%SK_11*i1Tt1%zG<+o(CYZxr;=6GhxqB8w>qsX2A^=+>!gecAoc)PCdQv1fs(b5A6F zs~7D2bm+cuaCWX$R`2I$cD`rR4-K4N^Q|~8a$b97=&-nVAw4(VIG?Vq4un11oh~p| zb(4Wh15yf?Au=Oix_S|9bTsR`%?!$bnX;slTbCa$Is2fRZEJFN{y zxCBT_YjPbQXr=3A_`fehG8+HoBMl2-=im%sr63F#mFT{*#(kCNu>2-qKrN(^thF3w z*s)-NOGU6+Vx*+44gbgmC-l-%HU3ZQss&TWFQgw{5r6UCbVK~nB_MS!R0&fnU?^dx z1P=v>+)=b?4vh)jz%ea^GC-IYBo~rlLjxrWz=@}-39goO%Q}DizVx$cEi?tDw%&=( z!VT#!(-r&2Ako3pMLh7|sX4zWEZ0t3WF3xS)4^w;?E=|?fWLq&G6AxPgJdlPxfcqG z1r(|9DOyzb|7?BtZYj*-4cDa`J6C-$y<{Yz@f*D6)1oY`E)fOPlB38qr@%zTP;|l_ zRRVHED+afypca@w2%tD9SQW-0ji^VhEBJdL_`zs znXilhmUeeP=6^}ME_L{EVjQb3|MfX1X!6USYz)OC1K7jT35zYFu#GvC2<`wPQgD$H z&u8!={$slKkWjh;1yvYL2VTsvD$}JS_^s383tvmOb{6kRvqn#Mq@w;%!%uR_19b*= zP$?AABlWQeZZx*6j(=oHk?lYmQ3Nee`5GXGyCago_68d24q5wJ+~9HFRmrA!z6sl$akIYGBK){r{6h+5sqkHURwrxa^uqp7?c1^`e)!R>zk2bU z{(-3gm4mlVPf<~r^TDk2D!fB$LX?8>!EaHEZ+E1 zI#@k%H#Q7XfkAp9LxM0DaNhyu(?Bes*$pfckl!J>0%vREEC`E3t&~a$cob*jfiGsm z8SuLr{rVZ5kNq~?zala5vQ}m$r6)l`q?fQQB;=?Df+;9|bSP3RjG?y$uV<}L46z<& zgIWniuQ6l9m$e&VuOZms$61mV_{7$<1DdZ;#Hbg4OCZOI_eQL*`_`%f9& z+Mc_%2}F3=oppY_JiBwSrO^X1+4cLDk4BH04{R)35^(5OI;v`t&_-?srcq| zv!S!xRBLu-Y42azS7co4ak`WW)W)suqw#LrbQOBtM6+`*3y1nIWViE6+zZv`x^r;u zo3f4Z=3gbJR`2D#ao0K7^6CS8TW9;Z*?*>IzT!E%d(ie{4w8H9BS z%8BfED~|^Zv2SN~ZM^s`*_!ym$`pu&7k1?ykMf2el1t#mDFs(nuncHXozlZ{@y*e!kMJ(p#-Bysl- zlilY-5tdRGb=Gb%a~vWh=V&y9bS3o;mI_EHl!aEs8`cPPT&>&TP%j8b(T@fxjWPWa_P`YU zS1`z{D|1Kp(siy43I1nUPv`Mn+0ir}_(puh% zD0o*R5&>uwfKm_zf|5W)fM5lkLcM7MIfiHi89oMqR(FgKzb3`GeBbLjE3e9~Srs?$ zM_%ypW;WKjtyYx#fC86C$`rBsdu}@@P+waYWYOLK1tVIn@uFX*0YD=U#98nJ74)#_REbG|Jkld9E}wS!*Rla$lZhg z5Lx08Bs?=HTH$bqAz^ZeFgTMKR-Ag(Sc*sigXvYbleYIaHuTiSnc!q^Bwlt_+S_@5 zf8(y?1T>zpwn2O1`VEbr$8U@@j>da_k-l}Zp!EqJj&tDC>%auGhX=CwyF_jYdk=RI zFs}}oGuYP{WE(J3A_PR2aPoBtwAjb_hCZ6#0g#@)3c>j;N$ z$KeS&Ti}cg=O@Uuq4)@nmT;8B5SipqI*ec@C6(wAq?!=(3`ZP{YE@W4ad+g&!_~fL z2LE54xIPK1ZhPjU&SR%H{^-iir><{Yv!=7{#>P#{;^L&h+8Nxk=~n?TxE@ F{|Dcy9gF|~ delta 4227 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 h+FD|$RUzZvWo, Service>), +} + +/// Cloud services are a optional feature that allows you to interact with the cloud services +/// of Spacedrive. +/// They're optional in two different ways: +/// - The cloud services depends on a user being logged in with our server. +/// - The user being connected to the internet to begin with. +/// As we don't want to force the user to be connected to the internet, we have to make sure +/// that core can always operate without the cloud services. +#[derive(Debug, Clone)] +pub struct CloudServices { + client_state: Arc>, + get_cloud_api_address: Url, + http_client: reqwest::Client, + domain_name: String, +} + +impl CloudServices { + /// Creates a new cloud services client that can be used to interact with the cloud services. + /// The client will try to connect to the cloud services on a best effort basis, as the user + /// might not be connected to the internet. + /// If the client fails to connect, it will try again the next time it's used. + pub async fn new( + get_cloud_api_address: impl IntoUrl, + domain_name: String, + ) -> Result { + let http_client_builder = reqwest::Client::builder().timeout(Duration::from_secs(3)); + + #[cfg(not(debug_assertions))] + { + builder = builder.https_only(true); + } + + let http_client = http_client_builder.build().map_err(Error::HttpClientInit)?; + let get_cloud_api_address = get_cloud_api_address + .into_url() + .map_err(Error::InvalidUrl)?; + + let client_state = match Self::init_client( + &http_client, + get_cloud_api_address.clone(), + domain_name.clone(), + ) + .await + { + Ok(client) => Arc::new(RwLock::new(ClientState::Connected(client))), + Err(e) => { + warn!( + ?e, + "Failed to initialize cloud services client; \ + This is a best effort and we will continue in Not Connected mode" + ); + Arc::new(RwLock::new(ClientState::NotConnected)) + } + }; + + Ok(Self { + client_state, + get_cloud_api_address, + http_client, + domain_name, + }) + } + + async fn init_client( + http_client: &reqwest::Client, + get_cloud_api_address: Url, + domain_name: String, + ) -> Result, Service>, Error> { + let cloud_api_address = http_client + .get(get_cloud_api_address) + .send() + .await + .map_err(Error::FailedToRequestApiAddress)? + .error_for_status() + .map_err(Error::AuthServerError)? + .text() + .await + .map_err(Error::FailedToExtractApiAddress)? + .parse::()?; + + let crypto_config = { + #[cfg(debug_assertions)] + { + struct SkipServerVerification; + impl rustls_old::client::ServerCertVerifier for SkipServerVerification { + fn verify_server_cert( + &self, + _end_entity: &rustls_old::Certificate, + _intermediates: &[rustls_old::Certificate], + _server_name: &rustls_old::ServerName, + _scts: &mut dyn Iterator, + _ocsp_response: &[u8], + _now: std::time::SystemTime, + ) -> Result { + Ok(rustls_old::client::ServerCertVerified::assertion()) + } + } + + rustls_old::ClientConfig::builder() + .with_safe_defaults() + .with_custom_certificate_verifier(Arc::new(SkipServerVerification)) + .with_no_client_auth() + } + + #[cfg(not(debug_assertions))] + { + rustls_old::ClientConfig::builder() + .with_safe_defaults() + .with_no_client_auth() + } + }; + + let client_config = ClientConfig::new(Arc::new(crypto_config)); + + let mut endpoint = Endpoint::client("[::]:0".parse().expect("hardcoded address")) + .map_err(Error::FailedToCreateEndpoint)?; + endpoint.set_default_client_config(client_config); + + // TODO(@fogodev): It's possible that we can't keep the connection alive all the time, + // and need to use single shot connections. I will only be sure when we have + // actually battle-tested the cloud services in core. + Ok(Client::new(RpcClient::new(QuinnConnection::new( + endpoint, + cloud_api_address, + domain_name, + )))) + } + + /// Returns a client to the cloud services. + /// + /// If the client is not connected, it will try to connect to the cloud services. + /// Available routes documented in + /// [`sd_cloud_schema::Service`](https://github.com/spacedriveapp/cloud-services-schema). + pub async fn client(&self) -> Result, Service>, Error> { + if let ClientState::Connected(client) = &*self.client_state.read().await { + return Ok(client.clone()); + } + + // If we're not connected, we need to try to connect. + let client = Self::init_client( + &self.http_client, + self.get_cloud_api_address.clone(), + self.domain_name.clone(), + ) + .await?; + *self.client_state.write().await = ClientState::Connected(client.clone()); + + Ok(client) + } +} + +#[cfg(test)] +mod tests { + use sd_cloud_schema::{auth, devices}; + + use super::*; + + #[tokio::test] + async fn test_client() { + let response = CloudServices::new( + "http://localhost:9420/cloud-api-address", + "localhost".to_string(), + ) + .await + .unwrap() + .client() + .await + .unwrap() + .devices() + .list(devices::list::Request { + access_token: auth::AccessToken("invalid".to_string()), + }) + .await + .unwrap(); + + assert!(matches!( + response, + Err(sd_cloud_schema::Error::Client( + sd_cloud_schema::error::ClientSideError::Unauthorized + )) + )) + } +} diff --git a/core/src/lib.rs b/core/src/lib.rs index bac56e73f..57bfdd11e 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -6,6 +6,7 @@ use crate::{ location::LocationManagerError, }; +use sd_core_cloud_services::CloudServices; use sd_core_heavy_lifting::{media_processor::ThumbnailKind, JobSystem}; use sd_core_prisma_helpers::CasId; @@ -80,6 +81,7 @@ pub struct Node { pub http: reqwest::Client, pub task_system: TaskSystem, pub job_system: JobSystem>, + pub cloud_services: Arc, #[cfg(feature = "ai")] pub old_image_labeller: Option, } @@ -128,6 +130,25 @@ impl Node { let (old_jobs, jobs_actor) = old_job::OldJobs::new(); let libraries = library::Libraries::new(data_dir.join("libraries")).await?; + let (get_cloud_api_address, cloud_services_domain_name) = { + #[cfg(debug_assertions)] + { + ( + std::env::var("SD_CLOUD_API_ADDRESS_URL") + .unwrap_or_else(|_| "http://localhost:9420/cloud-api-address".to_string()), + std::env::var("SD_CLOUD_API_DOMAIN_NAME") + .unwrap_or_else(|_| "localhost".to_string()), + ) + } + #[cfg(not(debug_assertions))] + { + ( + "https://auth.spacedrive.com/cloud-api-address".to_string(), + "api.spacedrive.com".to_string(), + ) + } + }; + let task_system = TaskSystem::new(); let (p2p, start_p2p) = p2p::P2PManager::new(config.clone(), libraries.clone()) @@ -149,6 +170,9 @@ impl Node { )), http: reqwest::Client::new(), env, + cloud_services: Arc::new( + CloudServices::new(&get_cloud_api_address, cloud_services_domain_name).await?, + ), #[cfg(feature = "ai")] old_image_labeller: OldImageLabeler::new( YoloV8::model(image_labeler_version)?, @@ -441,6 +465,8 @@ pub enum NodeError { Logger(#[from] FromEnvError), #[error(transparent)] JobSystem(#[from] sd_core_heavy_lifting::JobSystemError), + #[error(transparent)] + CloudServices(#[from] sd_core_cloud_services::Error), #[cfg(feature = "ai")] #[error("ai error: {0}")] diff --git a/crates/ai/Cargo.toml b/crates/ai/Cargo.toml index a1cb437e6..edb1a36ea 100644 --- a/crates/ai/Cargo.toml +++ b/crates/ai/Cargo.toml @@ -36,12 +36,12 @@ thiserror = { workspace = true } tokio = { workspace = true, features = ["fs"] } tokio-stream = { workspace = true } tracing = { workspace = true } +url = { workspace = true } uuid = { workspace = true, features = ["v4", "serde"] } # Note: half and ndarray version must be the same as used in ort half = { version = "2.1", features = ['num-traits'] } ndarray = "0.15" -url = '2.5.0' # Microsoft does not provide a release for osx-gpu. See: https://github.com/microsoft/onnxruntime/releases # "gpu" means CUDA or TensorRT EP. Thus, the ort crate cannot download them at build time.