147 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			147 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
// SPDX-License-Identifier: Apache-2.0
 | 
						|
 | 
						|
use detee_shared::app_proto::brain_app_cli_client::BrainAppCliClient;
 | 
						|
use detee_shared::app_proto::{
 | 
						|
    AppContract, AppNodeFilters, AppNodeListResp, DelAppReq, ListAppContractsReq, NewAppReq,
 | 
						|
    NewAppRes,
 | 
						|
};
 | 
						|
use tokio_stream::StreamExt;
 | 
						|
use tonic::transport::Channel;
 | 
						|
 | 
						|
use crate::call_with_follow_redirect;
 | 
						|
use crate::config::Config;
 | 
						|
use crate::utils::{self, sign_request};
 | 
						|
 | 
						|
#[derive(thiserror::Error, Debug)]
 | 
						|
pub enum Error {
 | 
						|
    #[error("Failed to connect to the brain: {0}")]
 | 
						|
    BrainConnection(#[from] tonic::transport::Error),
 | 
						|
    #[error("Received error from brain: {}", _0.message())]
 | 
						|
    ResponseStatus(#[from] tonic::Status),
 | 
						|
    #[error(transparent)]
 | 
						|
    InternalError(#[from] utils::Error),
 | 
						|
    #[error(transparent)]
 | 
						|
    ConfigError(#[from] crate::config::Error),
 | 
						|
    #[error("The Root CA file got corrupted.")]
 | 
						|
    CorruptedRootCa(#[from] std::io::Error),
 | 
						|
    #[error("Internal app error: could not parse Brain URL")]
 | 
						|
    CorruptedBrainUrl,
 | 
						|
    #[error("Max redirects exceeded: {0}")]
 | 
						|
    MaxRedirectsExceeded(String),
 | 
						|
    #[error("Redirect error: {0}")]
 | 
						|
    RedirectError(String),
 | 
						|
}
 | 
						|
 | 
						|
type Result<T> = std::result::Result<T, Error>;
 | 
						|
 | 
						|
impl crate::HumanOutput for AppNodeListResp {
 | 
						|
    fn human_cli_print(&self) {
 | 
						|
        println!("The pubkey of this node is {} and the IP is {}", self.node_pubkey, self.ip);
 | 
						|
        println!("It belongs to the operator {}", self.operator);
 | 
						|
        println!(
 | 
						|
            "This node is located in the city {}, within the region of {}, in {}",
 | 
						|
            self.city, self.region, self.country
 | 
						|
        );
 | 
						|
        println!("The price multiplier for the node is {}.", self.price);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
impl crate::HumanOutput for AppContract {
 | 
						|
    fn human_cli_print(&self) {
 | 
						|
        let app_resource = self.resource.clone().unwrap_or_default();
 | 
						|
        let mapped_ports = self
 | 
						|
            .mapped_ports
 | 
						|
            .clone()
 | 
						|
            .iter()
 | 
						|
            .map(|p| format!("({},{})", p.host_port, p.guest_port))
 | 
						|
            .collect::<Vec<_>>()
 | 
						|
            .join(", ");
 | 
						|
        println!(
 | 
						|
            "The App {} has the UUID {}, and it runs on the node {}",
 | 
						|
            self.app_name, self.uuid, self.node_pubkey
 | 
						|
        );
 | 
						|
        println!("The app has mapped ports by the node are: {mapped_ports}");
 | 
						|
        println!(
 | 
						|
            "The App has {} vCPUS, {}MB of memory and a disk of {} GB.",
 | 
						|
            app_resource.vcpus,
 | 
						|
            app_resource.memory_mib,
 | 
						|
            app_resource.disk_size_mib / 1024
 | 
						|
        );
 | 
						|
        println!("You have locked {} nanocredits in the contract, that get collected at a rate of {} nanocredits per minute.",
 | 
						|
            self.locked_nano, self.nano_per_minute);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
async fn client() -> Result<BrainAppCliClient<Channel>> {
 | 
						|
    let default_brain_url = Config::get_brain_info().0;
 | 
						|
    Ok(BrainAppCliClient::new(Config::connect_brain_channel(default_brain_url).await?))
 | 
						|
}
 | 
						|
 | 
						|
async fn client_from_endpoint(reconnect_endpoint: String) -> Result<BrainAppCliClient<Channel>> {
 | 
						|
    Ok(BrainAppCliClient::new(Config::connect_brain_channel(reconnect_endpoint).await?))
 | 
						|
}
 | 
						|
 | 
						|
pub async fn new_app(req: NewAppReq) -> Result<NewAppRes> {
 | 
						|
    let client = client().await?;
 | 
						|
    match call_with_follow_redirect!(client, req, new_app).await {
 | 
						|
        Ok(res) => Ok(res.into_inner()),
 | 
						|
        Err(e) => {
 | 
						|
            log::error!("Failed to create new app: {}", e);
 | 
						|
            Err(e.into())
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
pub async fn delete_app(app_uuid: String) -> Result<()> {
 | 
						|
    let admin_pubkey = Config::get_detee_wallet()?;
 | 
						|
    let delete_req = DelAppReq { uuid: app_uuid, admin_pubkey };
 | 
						|
    let client = client().await?;
 | 
						|
    let _ = call_with_follow_redirect!(client, delete_req, delete_app).await?;
 | 
						|
    Ok(())
 | 
						|
}
 | 
						|
 | 
						|
pub async fn list_contracts(req: ListAppContractsReq) -> Result<Vec<AppContract>> {
 | 
						|
    let mut daemon_serivce = client().await?;
 | 
						|
    let mut res_stream = daemon_serivce.list_app_contracts(sign_request(req)?).await?.into_inner();
 | 
						|
 | 
						|
    let mut app_contracts = vec![];
 | 
						|
 | 
						|
    while let Some(stream_update) = res_stream.next().await {
 | 
						|
        match stream_update {
 | 
						|
            Ok(contract) => {
 | 
						|
                app_contracts.push(contract);
 | 
						|
            }
 | 
						|
            Err(e) => {
 | 
						|
                eprintln!("Brain disconnected from register_node: {e}");
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    Ok(app_contracts)
 | 
						|
}
 | 
						|
 | 
						|
pub async fn get_one_app_node(req: AppNodeFilters) -> Result<AppNodeListResp> {
 | 
						|
    let res = client().await?.get_one_app_node(sign_request(req)?).await?;
 | 
						|
    Ok(res.into_inner())
 | 
						|
}
 | 
						|
 | 
						|
pub async fn get_app_node_list(req: AppNodeFilters) -> Result<Vec<AppNodeListResp>> {
 | 
						|
    log::debug!("Getting app nodes from brain...");
 | 
						|
    let mut nodes = Vec::new();
 | 
						|
    let mut grpc_stream = client().await?.list_app_nodes(sign_request(req)?).await?.into_inner();
 | 
						|
 | 
						|
    while let Some(stream_update) = grpc_stream.next().await {
 | 
						|
        match stream_update {
 | 
						|
            Ok(node) => {
 | 
						|
                log::debug!("Received node from brain: {node:?}");
 | 
						|
                nodes.push(node);
 | 
						|
            }
 | 
						|
            Err(e) => {
 | 
						|
                log::warn!("Received error instead of node list: {e:?}");
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    log::debug!("Brain terminated list_nodes stream.");
 | 
						|
    Ok(nodes)
 | 
						|
}
 |