diff --git a/cli/Cargo.lock b/cli/Cargo.lock index a2d8968..6e951ca 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -278,9 +278,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.51" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5" +checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" dependencies = [ "clap_builder", "clap_derive", @@ -288,9 +288,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.51" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a" +checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" dependencies = [ "anstream", "anstyle", @@ -1309,9 +1309,9 @@ dependencies = [ [[package]] name = "prost" -version = "0.14.1" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7231bd9b3d3d33c86b58adbac74b5ec0ad9f496b19d22801d773636feaa95f3d" +checksum = "d2ea70524a2f82d518bce41317d0fae74151505651af45faf1ffbd6fd33f0568" dependencies = [ "bytes", "prost-derive", @@ -1341,9 +1341,9 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.14.1" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9120690fafc389a67ba3803df527d0ec9cbbc9cc45e4cc20b332996dfb672425" +checksum = "27c6023962132f4b30eb4c172c91ce92d933da334c59c23cddee82358ddafb0b" dependencies = [ "anyhow", "itertools", @@ -1354,9 +1354,9 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.14.1" +version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9b4db3d6da204ed77bb26ba83b6122a73aeb2e87e25fbf7ad2e84c4ccbf8f72" +checksum = "8991c4cbdb8bc5b11f0b074ffe286c30e523de90fee5ba8132f1399f23cb3dd7" dependencies = [ "prost", ] @@ -1794,9 +1794,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.48.0" +version = "1.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" dependencies = [ "bytes", "libc", @@ -1987,9 +1987,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "log", "pin-project-lite", @@ -1999,9 +1999,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", @@ -2010,9 +2010,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.34" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", "valuable", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 2a43cb9..cfbcae0 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -11,17 +11,17 @@ license = "Apache-2.0" readme = "../README.md" [dependencies] -clap = { version = "4.5.51", features = ["derive"] } +clap = { version = "4.5.54", features = ["derive"] } colored = "3.0.0" directories = "6.0.0" serde = { version = "1.0.219", features = ["derive"] } -tracing = "0.1.41" -tokio = {version = "1.47.0",features = ["macros",'rt-multi-thread']} +tracing = "0.1.44" +tokio = {version = "1.49.0",features = ["macros",'rt-multi-thread']} anyhow = "1.0.100" tonic = "0.14.2" tonic-reflection = "0.14.2" -prost-types = "0.14.1" -prost = "0.14.1" +prost-types = "0.14.3" +prost = "0.14.3" cortexflow_agent_api = {version = "0.1.1",features = ["client"]} kube = "2.0.1" k8s-openapi = {version = "0.26.0", features = ["v1_34"]} diff --git a/cli/src/errors.rs b/cli/src/errors.rs new file mode 100644 index 0000000..b813e35 --- /dev/null +++ b/cli/src/errors.rs @@ -0,0 +1,124 @@ +use colored::Colorize; +use std::{error::Error, fmt}; + +// docs: +// +// CliError enum to group all the errors +// +// Custom error definition +// +// BaseError: +// - used for general errors +// +// InstallerError: +// - used for general installation errors occured during the installation of cortexflow components. Can be used for: +// - Return downloading errors +// - Return unsuccessful file removal during installation +// +// ClientError: +// - used for Kubernetes client errors. Can be used for: +// - Return client connection errors +// +// AgentError: +// - used for cortexflow agent errors. Can be used for: +// - return errors from the reflection server +// - return unavailable agent errors (404) +// +// +// implements fmt::Display for user friendly error messages + +#[derive(Debug)] +pub enum CliError { + InstallerError { reason: String }, + ClientError(kube::Error), + AgentError(tonic_reflection::server::Error), + BaseError { reason: String }, +} +// docs: +// +// The following functions implements the trait From conversions +// +// The From Trait is used to perform a value-to-value conversion while consuming input values. +// We use that to return a single error type 'CliError' that incapsulates multiple error types + +impl From for CliError { + fn from(e: kube::Error) -> Self { + CliError::ClientError(e) + } +} +impl From for CliError { + fn from(e: anyhow::Error) -> Self { + CliError::BaseError { + reason: e.to_string(), + } + } +} +impl From for CliError { + fn from(e: prost::DecodeError) -> Self { + return CliError::AgentError(tonic_reflection::server::Error::DecodeError(e)); + } +} +impl From for CliError { + fn from(e: tonic::Status) -> Self { + return CliError::BaseError { + reason: e.to_string(), + }; + } +} + +// docs: +// +// The Trait fmt::Display is used to create a user friendly error message for the CliError type. +// This Trait automatically implements the ToString trait for the type allowing +// the usage of .to_string() method + +impl fmt::Display for CliError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + CliError::InstallerError { reason } => { + write!( + f, + "{} {} {}", + "=====>".blue().bold(), + "An error occured while installing cortexflow components. Reason:" + .bold() + .red(), + reason.red().bold() + ) + } + CliError::BaseError { reason } => { + write!( + f, + "{} {} {}", + "=====>".blue().bold(), + "An error occured. Reason:" + .bold() + .red(), + reason.red().bold() + ) + } + CliError::ClientError(e) => { + // raw error looks like this + // (ErrorResponse { status: "failed", message: "Failed to connect to kubernetes client", reason: "transport error", code: 404 } + let msg = Error::source(e).unwrap(); // msg = Failed to connect to kubernetes client: transport error + write!( + f, + "{} {} {}", + "=====>".blue().bold(), + "Client Error:".bold().red(), + msg.to_string().red().bold() + ) + } + CliError::AgentError(e) => { + let msg = Error::source(e).unwrap(); + write!( + f, + "{} {} {}", + "=====>".bold().blue(), + "Agent Error:".bold().red(), + msg.to_string().bold().red() + ) + } + } + } +} diff --git a/cli/src/essential.rs b/cli/src/essential.rs index 3f43350..5ca01b9 100644 --- a/cli/src/essential.rs +++ b/cli/src/essential.rs @@ -1,12 +1,10 @@ +use crate::errors::CliError; use std::borrow::Cow; -use std::process::Output; use std::thread; use std::time::Duration; -use std::{collections::BTreeMap, fmt, process::Command, result::Result::Ok}; +use std::{collections::BTreeMap, process::Command, result::Result::Ok}; -use anyhow::Error; use colored::Colorize; -use k8s_openapi::apimachinery::pkg::version; use kube::core::ErrorResponse; use serde::Serialize; @@ -17,97 +15,6 @@ use kube::client::Client; pub static BASE_COMMAND: &str = "kubectl"; // docs: Kubernetes base command -// docs: -// -// CliError enum to group all the errors -// -// Custom error definition -// InstallerError: -// - used for general installation errors occured during the installation of cortexflow components. Can be used for: -// - Return downloading errors -// - Return unsuccessful file removal during installation -// -// ClientError: -// - used for Kubernetes client errors. Can be used for: -// - Return client connection errors -// -// UninstallError: -// - used for general installation errors occured during the uninstall for cortexflow components. Can be used for: -// - Return components removal errors -// -// AgentError: -// - used for cortexflow agent errors. Can be used for: -// - return errors from the reflection server -// - return unavailable agent errors (404) -// -// MonitoringError: -// - used for general monitoring errors. TODO: currently under implementation -// -// implements fmt::Display for user friendly error messages - -#[derive(Debug)] -pub enum CliError { - InstallerError { reason: String }, - ClientError(kube::Error), - UninstallError { reason: String }, - AgentError(tonic_reflection::server::Error), - MonitoringError { reason: String }, -} -// docs: -// error type conversions - -impl From for CliError { - fn from(e: kube::Error) -> Self { - CliError::ClientError(e) - } -} -impl From for CliError { - fn from(e: anyhow::Error) -> Self { - CliError::MonitoringError { - reason: format!("{}", e), - } - } -} -impl From<()> for CliError { - fn from(v: ()) -> Self { - return ().into(); - } -} - -// docs: -// fmt::Display implementation for CliError type. Creates a user friendly message error message. -// TODO: implement colored messages using the colorize crate for better output display - -impl fmt::Display for CliError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - CliError::InstallerError { reason } => { - write!( - f, - "An error occured while installing cortexflow components. Reason: {}", - reason - ) - } - CliError::UninstallError { reason } => { - write!( - f, - "An error occured while installing cortexflow components. Reason: {}", - reason - ) - } - CliError::MonitoringError { reason } => { - write!( - f, - "An error occured while installing cortexflow components. Reason: {}", - reason - ) - } - CliError::ClientError(e) => write!(f, "Client Error: {}", e), - CliError::AgentError(e) => write!(f, "Agent Error: {}", e), - } - } -} - #[derive(Serialize)] pub struct MetadataConfigFile { blocklist: Vec, @@ -143,7 +50,7 @@ pub async fn connect_to_client() -> Result { // // Returns an error if the command fails -pub fn update_cli() { +pub fn update_cli() -> Result<(), CliError> { let latest_version = get_latest_cfcli_version().expect("Can't get the latest version"); println!("{} {}", "=====>".blue().bold(), "Updating CortexFlow CLI"); println!( @@ -158,10 +65,12 @@ pub fn update_cli() { .expect("error"); if !output.status.success() { - eprintln!( - "Error extracting the version : {}", - String::from_utf8_lossy(&output.stderr) - ); + return Err(CliError::InstallerError { + reason: format!( + "Error extracting the version : {}", + String::from_utf8_lossy(&output.stderr) + ), + }); } else { // extract the cli version: let version = String::from_utf8_lossy(&output.stdout) @@ -199,10 +108,12 @@ pub fn update_cli() { .output() .expect("error"); if !update_command.status.success() { - eprintln!( - "Error updating the CLI: {} ", - String::from_utf8_lossy(&update_command.stderr) - ); + return Err(CliError::InstallerError { + reason: format!( + "Error updating the CLI: {} ", + String::from_utf8_lossy(&update_command.stderr) + ), + }); } else { println!( "{} {}", @@ -212,21 +123,22 @@ pub fn update_cli() { } } } + Ok(()) } // docs: // // This function returns the latest version of the CLI from the crates.io registry -pub fn get_latest_cfcli_version() -> Result { +pub fn get_latest_cfcli_version() -> Result { let output = Command::new("cargo") .args(["search", "cortexflow-cli", "--limit", "1"]) .output() .expect("Error"); if !output.status.success() { - return Err(Error::msg(format!( - "An error occured during the latest version extraction" - ))); + return Err(CliError::InstallerError { + reason: "Cannot extract the latest version".to_string(), + }); } else { let command_stdout = String::from_utf8_lossy(&output.stdout); @@ -323,10 +235,10 @@ pub async fn read_configs() -> Result, CliError> { Ok(Vec::new()) //in case the key fails } - Err(_) => Err(CliError::ClientError(kube::Error::Api(ErrorResponse { + Err(e) => Err(CliError::ClientError(kube::Error::Api(ErrorResponse { status: "failed".to_string(), message: "Failed to connect to kubernetes client".to_string(), - reason: "Your cluster is probably disconnected".to_string(), + reason: e.to_string(), code: 404, }))), } @@ -351,7 +263,7 @@ pub async fn create_config_file(config_struct: MetadataConfigFile) -> Result<(), match connect_to_client().await { Ok(client) => { let namespace = "cortexflow"; - let configmap = "cortexbrain-client-config"; + //let configmap = "cortexbrain-client-config"; let api: Api = Api::namespaced(client, namespace); @@ -373,15 +285,20 @@ pub async fn create_config_file(config_struct: MetadataConfigFile) -> Result<(), println!("Configmap created successfully"); } Err(e) => { - eprintln!("An error occured: {}", e); + return Err(CliError::ClientError(kube::Error::Api(ErrorResponse { + status: "failed".to_string(), + message: "Failed to create configmap".to_string(), + reason: e.to_string(), + code: 404, + }))); } } Ok(()) } - Err(_) => Err(CliError::ClientError(kube::Error::Api(ErrorResponse { + Err(e) => Err(CliError::ClientError(kube::Error::Api(ErrorResponse { status: "failed".to_string(), message: "Failed to connect to kubernetes client".to_string(), - reason: "Your cluster is probably disconnected".to_string(), + reason: e.to_string(), code: 404, }))), } @@ -422,7 +339,9 @@ pub async fn update_config_metadata(input: &str, action: &str) -> Result<(), Cli if let Some(index) = ips.iter().position(|target| target == &input.to_string()) { ips.remove(index); } else { - eprintln!("Index of element not found"); + return Err(CliError::BaseError { + reason: "Index of element not found".to_string(), + }); } // override blocklist parameters @@ -472,17 +391,18 @@ pub async fn update_configmap(config_struct: MetadataConfigFile) -> Result<(), C println!("Map updated successfully"); } Err(e) => { - eprintln!("An error occured during the patching process: {}", e); - return Err(e.into()); + return Err(CliError::BaseError { + reason: format!("An error occured during the patching process: {}", e), + }); } } Ok(()) } - Err(_) => Err(CliError::ClientError(kube::Error::Api(ErrorResponse { + Err(e) => Err(CliError::ClientError(kube::Error::Api(ErrorResponse { status: "failed".to_string(), message: "Failed to connect to kubernetes client".to_string(), - reason: "Your cluster is probably disconnected".to_string(), + reason: e.to_string(), code: 404, }))), } diff --git a/cli/src/install.rs b/cli/src/install.rs index a24fc22..bdb1ea1 100644 --- a/cli/src/install.rs +++ b/cli/src/install.rs @@ -1,7 +1,6 @@ -use crate::essential::{ - BASE_COMMAND, CliError, connect_to_client, create_config_file, create_configs, -}; -use clap::{Args, Subcommand, command}; +use crate::errors::CliError; +use crate::essential::{BASE_COMMAND, connect_to_client, create_config_file, create_configs}; +use clap::{Args, Subcommand}; use colored::Colorize; use kube::Error; use kube::core::ErrorResponse; @@ -42,7 +41,7 @@ pub enum InstallCommands { } //install args -#[derive(Args, Debug, Clone)] +#[derive(Args, Debug)] pub struct InstallArgs { #[command(subcommand)] pub install_cmd: InstallCommands, @@ -144,12 +143,16 @@ async fn install_cluster_components() -> Result<(), CliError> { ); Ok(()) } - Err(e) => Err(CliError::ClientError(Error::Api(ErrorResponse { - status: "failed".to_string(), - message: "Failed to connect to kubernetes client".to_string(), - reason: "Your cluster is probably disconnected".to_string(), - code: 404, - }))), + Err(e) => { + return { + Err(CliError::ClientError(Error::Api(ErrorResponse { + status: "failed".to_string(), + message: "Failed to connect to kubernetes client".to_string(), + reason: e.to_string(), + code: 404, + }))) + }; + } } } @@ -190,12 +193,16 @@ async fn install_simple_example_component() -> Result<(), CliError> { ); Ok(()) } - Err(e) => Err(CliError::ClientError(Error::Api(ErrorResponse { - status: "failed".to_string(), - message: "Failed to connect to kubernetes client".to_string(), - reason: "Your cluster is probably disconnected".to_string(), - code: 404, - }))), + Err(e) => { + return { + Err(CliError::ClientError(Error::Api(ErrorResponse { + status: "failed".to_string(), + message: "Failed to connect to kubernetes client".to_string(), + reason: e.to_string(), + code: 404, + }))) + }; + } } } @@ -238,7 +245,7 @@ fn install_components(components_type: &str) -> Result<(), CliError> { "Applying", component.to_string().green().bold() ); - apply_component(component); + apply_component(component)?; i = i + 1; } } else if components_type == "simple-example" { @@ -258,7 +265,7 @@ fn install_components(components_type: &str) -> Result<(), CliError> { "Applying", component.to_string().green().bold() ); - apply_component(component); + apply_component(component)?; i = i + 1; } } else { @@ -282,16 +289,18 @@ fn apply_component(file: &str) -> Result<(), CliError> { let output = Command::new(BASE_COMMAND) .args(["apply", "-f", file]) .output() - .map_err(|_| CliError::InstallerError { - reason: "Can't install component from file".to_string(), + .map_err(|e| CliError::InstallerError { + reason: e.to_string(), })?; if !output.status.success() { - eprintln!( - "Error installing file: {}:\n{}", - file, - String::from_utf8_lossy(&output.stderr) - ); + return Err(CliError::InstallerError { + reason: format!( + "Error installing file: {}:\n{}", + file, + String::from_utf8_lossy(&output.stderr) + ), + }); } else { println!("✅ Applied {}", file); } @@ -366,16 +375,18 @@ fn download_file(src: &str) -> Result<(), CliError> { Command::new("wget") .args([src]) .output() - .map_err(|_| CliError::InstallerError { - reason: "An error occured: component download failed".to_string(), + .map_err(|e| CliError::InstallerError { + reason: e.to_string(), })?; if !output.status.success() { - eprintln!( - "Error copying file: {}.\n{}", - src, - String::from_utf8_lossy(&output.stderr) - ); + return Err(CliError::InstallerError { + reason: format!( + "Error copying file: {}.\n{}", + src, + String::from_utf8_lossy(&output.stderr) + ), + }); } else { println!("✅ Copied file from {} ", src); } @@ -396,16 +407,18 @@ fn rm_file(file_to_remove: &str) -> Result<(), CliError> { let output = Command::new("rm") .args(["-f", file_to_remove]) .output() - .map_err(|_| CliError::InstallerError { - reason: "cannot remove temporary installation file".to_string(), + .map_err(|e| CliError::InstallerError { + reason: e.to_string(), })?; if !output.status.success() { - eprintln!( - "Error removing file: {}:\n{}", - file_to_remove, - String::from_utf8_lossy(&output.stderr) - ); + return Err(CliError::InstallerError { + reason: format!( + "Error removing file: {}:\n{}", + file_to_remove, + String::from_utf8_lossy(&output.stderr) + ), + }); } else { println!("✅ Removed file {}", file_to_remove); } diff --git a/cli/src/logs.rs b/cli/src/logs.rs index bd819cc..102d97b 100644 --- a/cli/src/logs.rs +++ b/cli/src/logs.rs @@ -1,8 +1,9 @@ -use std::{ str, process::Command, result::Result::Ok }; -use colored::Colorize; +use crate::errors::CliError; +use crate::essential::{BASE_COMMAND, connect_to_client}; use clap::Args; -use kube::{ Error, core::ErrorResponse }; -use crate::essential::{ connect_to_client, BASE_COMMAND, CliError }; +use colored::Colorize; +use kube::{Error, core::ErrorResponse}; +use std::{process::Command, result::Result::Ok, str}; #[derive(Args, Debug, Clone)] pub struct LogsArgs { @@ -53,7 +54,7 @@ impl Component { pub async fn logs_command( service: Option, component: Option, - namespace: Option + namespace: Option, ) -> Result<(), CliError> { match connect_to_client().await { Ok(_) => { @@ -92,12 +93,18 @@ pub async fn logs_command( .collect() } (Some(service_name), None) => { - println!("Getting logs for service '{}' in namespace '{}'", service_name, ns); + println!( + "Getting logs for service '{}' in namespace '{}'", + service_name, ns + ); get_pods_for_service(&ns, &service_name).await? } (None, Some(component_str)) => { let comp = Component::from(component_str); - println!("Getting logs for component '{:?}' in namespace '{}'", comp, ns); + println!( + "Getting logs for component '{:?}' in namespace '{}'", + comp, ns + ); get_pods_for_component(&ns, &comp).await? } (None, None) => { @@ -117,8 +124,9 @@ pub async fn logs_command( for pod in pods { println!("{} Logs for pod: {:?}", "=====>".blue().bold(), pod); - match - Command::new(BASE_COMMAND).args(["logs", &pod, "-n", &ns, "--tail=50"]).output() + match Command::new(BASE_COMMAND) + .args(["logs", &pod, "-n", &ns, "--tail=50"]) + .output() { Ok(output) => { if output.status.success() { @@ -130,33 +138,34 @@ pub async fn logs_command( } } else { let stderr = str::from_utf8(&output.stderr).unwrap_or("Unknown error"); - eprintln!("Error getting logs for pod '{:?}': {}", pod, stderr); + return Err(CliError::BaseError { + reason: format!( + "Error getting logs for pod '{:?}': {}", + pod, stderr + ), + }); } } Err(err) => { - eprintln!( - "Failed to execute {} logs for pod '{:?}': {}", - BASE_COMMAND, - pod, - err - ); + return Err(CliError::BaseError { + reason: format!( + "Failed to execute {} logs for pod '{:?}': {}", + BASE_COMMAND, pod, err + ), + }); } } } Ok(()) } - Err(_) => { - Err( - CliError::ClientError( - Error::Api(ErrorResponse { - status: "failed".to_string(), - message: "Failed to connect to kubernetes client".to_string(), - reason: "Your cluster is probably disconnected".to_string(), - code: 404, - }) - ) - ) + Err(e) => { + return Err(CliError::ClientError(Error::Api(ErrorResponse { + status: "failed".to_string(), + message: "Failed to connect to kubernetes client".to_string(), + reason: e.to_string(), + code: 404, + }))); } } } @@ -174,24 +183,22 @@ pub async fn logs_command( pub async fn check_namespace_exists(namespace: &str) -> Result { match connect_to_client().await { Ok(_) => { - let output = Command::new(BASE_COMMAND).args(["get", "namespace", namespace]).output(); + let output = Command::new(BASE_COMMAND) + .args(["get", "namespace", namespace]) + .output(); match output { Ok(output) => Ok(output.status.success()), Err(_) => Ok(false), } } - Err(_) => { - Err( - CliError::ClientError( - Error::Api(ErrorResponse { - status: "failed".to_string(), - message: "Failed to connect to kubernetes client".to_string(), - reason: "Your cluster is probably disconnected".to_string(), - code: 404, - }) - ) - ) + Err(e) => { + return Err(CliError::ClientError(Error::Api(ErrorResponse { + status: "failed".to_string(), + message: "Failed to connect to kubernetes client".to_string(), + reason: e.to_string(), + code: 404, + }))); } } } @@ -232,17 +239,13 @@ pub async fn get_available_namespaces() -> Result, CliError> { _ => Ok(Vec::new()), } } - Err(_) => { - Err( - CliError::ClientError( - Error::Api(ErrorResponse { - status: "failed".to_string(), - message: "Failed to connect to kubernetes client".to_string(), - reason: "Your cluster is probably disconnected".to_string(), - code: 404, - }) - ) - ) + Err(e) => { + return Err(CliError::ClientError(Error::Api(ErrorResponse { + status: "failed".to_string(), + message: "Failed to connect to kubernetes client".to_string(), + reason: e.to_string(), + code: 404, + }))); } } } @@ -259,7 +262,7 @@ pub async fn get_available_namespaces() -> Result, CliError> { async fn get_pods_for_service( namespace: &str, - service_name: &str + service_name: &str, ) -> Result, CliError> { match connect_to_client().await { Ok(_) => { @@ -290,17 +293,13 @@ async fn get_pods_for_service( _ => Ok(Vec::new()), } } - Err(_) => { - Err( - CliError::ClientError( - Error::Api(ErrorResponse { - status: "failed".to_string(), - message: "Failed to connect to kubernetes client".to_string(), - reason: "Your cluster is probably disconnected".to_string(), - code: 404, - }) - ) - ) + Err(e) => { + return Err(CliError::ClientError(Error::Api(ErrorResponse { + status: "failed".to_string(), + message: "Failed to connect to kubernetes client".to_string(), + reason: e.to_string(), + code: 404, + }))); } } } @@ -318,7 +317,7 @@ async fn get_pods_for_service( async fn get_pods_for_component( namespace: &str, - component: &Component + component: &Component, ) -> Result, CliError> { match connect_to_client().await { Ok(_) => { @@ -349,17 +348,13 @@ async fn get_pods_for_component( _ => Ok(Vec::new()), } } - Err(_) => { - Err( - CliError::ClientError( - Error::Api(ErrorResponse { - status: "failed".to_string(), - message: "Failed to connect to kubernetes client".to_string(), - reason: "Your cluster is probably disconnected".to_string(), - code: 404, - }) - ) - ) + Err(e) => { + return Err(CliError::ClientError(Error::Api(ErrorResponse { + status: "failed".to_string(), + message: "Failed to connect to kubernetes client".to_string(), + reason: e.to_string(), + code: 404, + }))); } } } @@ -402,17 +397,13 @@ async fn get_all_pods(namespace: &str) -> Result, CliError> { _ => Ok(Vec::new()), } } - Err(_) => { - Err( - CliError::ClientError( - Error::Api(ErrorResponse { - status: "failed".to_string(), - message: "Failed to connect to kubernetes client".to_string(), - reason: "Your cluster is probably disconnected".to_string(), - code: 404, - }) - ) - ) + Err(e) => { + return Err(CliError::ClientError(Error::Api(ErrorResponse { + status: "failed".to_string(), + message: "Failed to connect to kubernetes client".to_string(), + reason: e.to_string(), + code: 404, + }))); } } } diff --git a/cli/src/main.rs b/cli/src/main.rs index 272123f..0a5ac46 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,4 +1,4 @@ -#![allow(warnings)] +mod errors; mod essential; mod install; mod logs; @@ -8,24 +8,23 @@ mod service; mod status; mod uninstall; -use clap::{ Args, Parser, Subcommand }; -use colored::Colorize; +use clap::{Args, Parser, Subcommand}; use std::result::Result::Ok; use tracing::debug; -use crate::essential::{ CliError, info, update_cli }; -use crate::install::{ InstallArgs, InstallCommands, install_cortexflow, install_simple_example }; -use crate::logs::{ LogsArgs, logs_command }; -use crate::monitoring::{ MonitorArgs, MonitorCommands, list_features, monitor_dropped_packets, monitor_identity_events, monitor_latency_metrics }; +use crate::errors::CliError; +use crate::essential::{info, update_cli}; +use crate::install::{InstallArgs, InstallCommands, install_cortexflow, install_simple_example}; +use crate::logs::{LogsArgs, logs_command}; +use crate::monitoring::{ + MonitorArgs, MonitorCommands, list_features, monitor_dropped_packets, monitor_identity_events, + monitor_latency_metrics, +}; use crate::policies::{ - PoliciesArgs, - PoliciesCommands, - check_blocklist, - create_blocklist, - remove_ip, + PoliciesArgs, PoliciesCommands, check_blocklist, create_blocklist, remove_ip, }; -use crate::service::{ ServiceArgs, ServiceCommands, describe_service, list_services }; -use crate::status::{ StatusArgs, status_command }; +use crate::service::{ServiceArgs, ServiceCommands, describe_service, list_services}; +use crate::status::{StatusArgs, status_command}; use crate::uninstall::uninstall; use crate::essential::update_config_metadata; @@ -43,23 +42,29 @@ struct Cli { cmd: Option, } -#[derive(Subcommand, Debug, Clone)] +#[derive(Subcommand, Debug)] enum Commands { /* list of available commands */ - #[command(name = "install", about = "Manage installation")] Install(InstallArgs), + #[command(name = "install", about = "Manage installation")] + Install(InstallArgs), #[command(name = "uninstall", about = "Manage uninstallation")] Uninstall, #[command(name = "update", about = "Check for updates")] Update, #[command(name = "info", about = "Check core info")] Info, - #[command(name = "service", about = "Manage services")] Service(ServiceArgs), - #[command(name = "status", about = "Check components status")] Status(StatusArgs), - #[command(name = "logs", about = "Check services logs")] Logs(LogsArgs), - #[command(name = "monitoring", about = "Monitoring commands")] Monitor(MonitorArgs), - #[command(name = "policy", about = "Network Policies")] Policies(PoliciesArgs), + #[command(name = "service", about = "Manage services")] + Service(ServiceArgs), + #[command(name = "status", about = "Check components status")] + Status(StatusArgs), + #[command(name = "logs", about = "Check services logs")] + Logs(LogsArgs), + #[command(name = "monitoring", about = "Monitoring commands")] + Monitor(MonitorArgs), + #[command(name = "policy", about = "Network Policies")] + Policies(PoliciesArgs), } -#[derive(Args, Debug, Clone)] +#[derive(Args)] struct SetArgs { val: String, } @@ -68,64 +73,66 @@ async fn args_parser() -> Result<(), CliError> { let args = Cli::parse(); debug!("Arguments {:?}", args.cmd); match args.cmd { - Some(Commands::Install(installation_args)) => - match installation_args.install_cmd { - InstallCommands::All => { - install_cortexflow().await.map_err(|e| eprintln!("{}",e) )?; - } - InstallCommands::TestPods => { - install_simple_example().await.map_err(|e| eprintln!("{}",e) )?; - } + Some(Commands::Install(installation_args)) => match installation_args.install_cmd { + InstallCommands::All => { + install_cortexflow().await?; } + InstallCommands::TestPods => { + install_simple_example().await?; + } + }, Some(Commands::Uninstall) => { - uninstall().await.map_err(|e| eprintln!("{}",e) )?; + uninstall().await?; } Some(Commands::Update) => { - update_cli(); + update_cli()?; } Some(Commands::Info) => { info(); } - Some(Commands::Service(service_args)) => - match service_args.service_cmd { - ServiceCommands::List { namespace } => { - list_services(namespace).await.map_err(|e| eprintln!("{}",e) )?; - } - ServiceCommands::Describe { service_name, namespace } => { - describe_service(service_name, &namespace).await.map_err(|e| eprintln!("{}",e) )?; - } + Some(Commands::Service(service_args)) => match service_args.service_cmd { + ServiceCommands::List { namespace } => { + list_services(namespace).await?; } + ServiceCommands::Describe { + service_name, + namespace, + } => { + describe_service(service_name, &namespace).await?; + } + }, Some(Commands::Status(status_args)) => { - status_command(status_args.output, status_args.namespace).await.map_err(|e| eprintln!("{}",e) )?; + status_command(status_args.output, status_args.namespace).await?; } Some(Commands::Logs(logs_args)) => { - logs_command(logs_args.service, logs_args.component, logs_args.namespace).await.map_err(|e| eprintln!("{}",e) )?; + logs_command(logs_args.service, logs_args.component, logs_args.namespace).await?; } - Some(Commands::Monitor(monitor_args)) => - match monitor_args.monitor_cmd { - MonitorCommands::List => { - let _ = list_features().await.map_err(|e| eprintln!("{}",e) )?; - } - MonitorCommands::Connections => { - let _ = monitor_identity_events().await.map_err(|e| eprintln!("{}",e) )?; - } - MonitorCommands::Latencymetrics => { - let _ = monitor_latency_metrics().await.map_err(|e| eprintln!("{}",e) )?; - } - MonitorCommands::Droppedpackets => { - let _ = monitor_dropped_packets().await.map_err(|e| eprintln!("{}",e) )?; - } + Some(Commands::Monitor(monitor_args)) => match monitor_args.monitor_cmd { + MonitorCommands::List => { + let _ = list_features().await?; + } + MonitorCommands::Connections => { + let _ = monitor_identity_events().await?; } + MonitorCommands::Latencymetrics => { + let _ = monitor_latency_metrics().await?; + } + MonitorCommands::Droppedpackets => { + let _ = monitor_dropped_packets().await?; + } + }, Some(Commands::Policies(policies_args)) => { match policies_args.policy_cmd { PoliciesCommands::CheckBlocklist => { - let _ = check_blocklist().await.map_err(|e| eprintln!("{}",e) )?; + let _ = check_blocklist().await?; } PoliciesCommands::CreateBlocklist => { // pass the ip as a monitoring flag match policies_args.flags { None => { - eprintln!("{}", "Insert at least one ip to create a blocklist".red()); + return Err(CliError::BaseError { + reason: "Insert at least one ip to create a blocklist".to_string(), + }); } Some(ip) => { println!("inserted ip: {} ", ip); @@ -133,39 +140,44 @@ async fn args_parser() -> Result<(), CliError> { match create_blocklist(&ip).await { Ok(_) => { //update the config metadata - let _ = update_config_metadata(&ip, "add").await.map_err(|e| eprintln!("{}",e) )?; + let _ = update_config_metadata(&ip, "add").await?; } Err(e) => { - eprintln!("{}", e); + return Err(CliError::BaseError { + reason: e.to_string(), + }); } } } } } - PoliciesCommands::RemoveIpFromBlocklist => - match policies_args.flags { - None => { - eprintln!( - "{}", - "Insert at least one ip to remove from the blocklist".red() - ); - } - Some(ip) => { - println!("Inserted ip: {}", ip); - match remove_ip(&ip).await { - Ok(_) => { - let _ = update_config_metadata(&ip, "delete").await.map_err(|e| eprintln!("{}",e) )?; - } - Err(e) => { - eprintln!("{}", e); - } + PoliciesCommands::RemoveIpFromBlocklist => match policies_args.flags { + None => { + return Err(CliError::BaseError { + reason: "Insert at least one ip to remove from the blocklist" + .to_string(), + }); + } + Some(ip) => { + println!("Inserted ip: {}", ip); + match remove_ip(&ip).await { + Ok(_) => { + let _ = update_config_metadata(&ip, "delete").await?; + } + Err(e) => { + return Err(CliError::BaseError { + reason: e.to_string(), + }); } } } + }, } } None => { - eprintln!("CLI unknown argument. Cli arguments passed: {:?}", args.cmd); + return Err(CliError::BaseError { + reason: format!("CLI unknown argument. Cli arguments passed: {:?}", args.cmd), + }); } } Ok(()) @@ -173,5 +185,5 @@ async fn args_parser() -> Result<(), CliError> { #[tokio::main] async fn main() { - let _ = args_parser().await; + let _ = args_parser().await.map_err(|e| eprintln!("{}", e)); } diff --git a/cli/src/mod.rs b/cli/src/mod.rs index 2c91fdc..fe7c816 100644 --- a/cli/src/mod.rs +++ b/cli/src/mod.rs @@ -5,4 +5,5 @@ pub mod service; pub mod status; pub mod logs; pub mod monitoring; -pub mod policies; \ No newline at end of file +pub mod policies; +pub mod errors; \ No newline at end of file diff --git a/cli/src/monitoring.rs b/cli/src/monitoring.rs index 506cc5f..b7cf3e2 100644 --- a/cli/src/monitoring.rs +++ b/cli/src/monitoring.rs @@ -1,9 +1,7 @@ -#![allow(warnings)] - //monitoring CLI function for identity service -use anyhow::Error; use colored::Colorize; use k8s_openapi::chrono::DateTime; +use kube::core::ErrorResponse; use prost::Message; use prost_types::FileDescriptorProto; use std::result::Result::Ok; @@ -12,8 +10,8 @@ use tonic_reflection::pb::v1::server_reflection_response::MessageResponse; use agent_api::client::{connect_to_client, connect_to_server_reflection}; use agent_api::requests::{get_all_features, send_active_connection_request}; -use clap::command; -use clap::{Args, Parser, Subcommand}; +use crate::errors::CliError; +use clap::{Args, Subcommand}; //monitoring subcommands #[derive(Subcommand, Debug, Clone)] @@ -23,15 +21,18 @@ pub enum MonitorCommands { #[command( name = "connections", about = "Monitor the recent connections detected by the identity service" - )] Connections, + )] + Connections, #[command( name = "latencymetrics", about = "Monitor the latency metrics detected by the metrics service" - )] Latencymetrics, + )] + Latencymetrics, #[command( name = "droppedpackets", about = "Monitor the dropped packets metrics detected by the metrics service" - )] Droppedpackets, + )] + Droppedpackets, } // cfcli monitor @@ -43,7 +44,7 @@ pub struct MonitorArgs { //pub flags: Option, } -pub async fn list_features() -> Result<(), Error> { +pub async fn list_features() -> Result<(), CliError> { match connect_to_server_reflection().await { Ok(client) => { println!( @@ -57,9 +58,8 @@ pub async fn list_features() -> Result<(), Error> { //decoding the proto file while let Some(resp) = streaming.message().await? { - if - let Some(MessageResponse::FileDescriptorResponse(fdr)) = - resp.message_response + if let Some(MessageResponse::FileDescriptorResponse(fdr)) = + resp.message_response { println!("Available services:"); for bytes in fdr.file_descriptor_proto { @@ -77,35 +77,38 @@ pub async fn list_features() -> Result<(), Error> { } } Err(e) => { - println!( - "{} {} {} {}", - "=====>".blue().bold(), - "An error occured".red(), - "Error:", - e - ); - return Err(e); + return Err(CliError::AgentError( + tonic_reflection::server::Error::InvalidFileDescriptorSet(e.to_string()), + )); } } } Err(e) => { - println!( - "{} {}", - "=====>".blue().bold(), - "Failed to connect to CortexFlow Server Reflection".red() - ); - return Err(e); + return Err(CliError::ClientError(kube::Error::Api(ErrorResponse { + status: "failed".to_string(), + message: "Failed to connect to kubernetes client".to_string(), + reason: e.to_string(), + code: 404, + }))); } } Ok(()) } -pub async fn monitor_identity_events() -> Result<(), Error> { - println!("{} {}", "=====>".blue().bold(), "Connecting to cortexflow Client".white()); +pub async fn monitor_identity_events() -> Result<(), CliError> { + println!( + "{} {}", + "=====>".blue().bold(), + "Connecting to cortexflow Client".white() + ); match connect_to_client().await { Ok(client) => { - println!("{} {}", "=====>".blue().bold(), "Connected to CortexFlow Client".green()); + println!( + "{} {}", + "=====>".blue().bold(), + "Connected to CortexFlow Client".green() + ); match send_active_connection_request(client).await { Ok(response) => { let resp = response.into_inner(); @@ -130,37 +133,40 @@ pub async fn monitor_identity_events() -> Result<(), Error> { } } Err(e) => { - println!( - "{} {} {} {}", - "=====>".blue().bold(), - "An error occured".red(), - "Error:", - e - ); - return Err(e); + return Err(CliError::AgentError( + tonic_reflection::server::Error::InvalidFileDescriptorSet(e.to_string()), + )); } } } Err(e) => { - println!( - "{} {}", - "=====>".blue().bold(), - "Failed to connect to CortexFlow Client".red() - ); - return Err(e); + return Err(CliError::ClientError(kube::Error::Api(ErrorResponse { + status: "failed".to_string(), + message: "Failed to connect to kubernetes client".to_string(), + reason: e.to_string(), + code: 404, + }))); } } Ok(()) } -pub async fn monitor_latency_metrics() -> Result<(), Error> { +pub async fn monitor_latency_metrics() -> Result<(), CliError> { //function to monitor latency metrics - println!("{} {}", "=====>".blue().bold(), "Connecting to cortexflow Client".white()); + println!( + "{} {}", + "=====>".blue().bold(), + "Connecting to cortexflow Client".white() + ); match connect_to_client().await { Ok(client) => { - println!("{} {}", "=====>".blue().bold(), "Connected to CortexFlow Client".green()); + println!( + "{} {}", + "=====>".blue().bold(), + "Connected to CortexFlow Client".green() + ); //send request to get latency metrics match agent_api::requests::send_latency_metrics_request(client).await { Ok(response) => { @@ -173,9 +179,10 @@ pub async fn monitor_latency_metrics() -> Result<(), Error> { "=====>".blue().bold(), resp.metrics.len() ); - + for (i, metric) in resp.metrics.iter().enumerate() { - let converted_timestamp= convert_timestamp_to_date(metric.timestamp_us); + let converted_timestamp = + convert_timestamp_to_date(metric.timestamp_us); println!( "{} Latency[{}] \n tgid: {} \n process_name: {} \n address_family: {} \n delta(us): {} \n src_address_v4: {} \n dst_address_v4: {} \n src_address_v6: {} \n dst_address_v6: {} \n local_port: {} \n remote_port: {} \n timestamp_us: {}\n", "=====>".blue().bold(), @@ -196,36 +203,39 @@ pub async fn monitor_latency_metrics() -> Result<(), Error> { } } Err(e) => { - println!( - "{} {} {} {}", - "=====>".blue().bold(), - "An error occured".red(), - "Error:", - e - ); - return Err(e); + return Err(CliError::AgentError( + tonic_reflection::server::Error::InvalidFileDescriptorSet(e.to_string()), + )); } } } Err(e) => { - println!( - "{} {}", - "=====>".blue().bold(), - "Failed to connect to CortexFlow Client".red() - ); - return Err(e); + return Err(CliError::ClientError(kube::Error::Api(ErrorResponse { + status: "failed".to_string(), + message: "Failed to connect to kubernetes client".to_string(), + reason: e.to_string(), + code: 404, + }))); } } Ok(()) } -pub async fn monitor_dropped_packets() -> Result<(), Error> { +pub async fn monitor_dropped_packets() -> Result<(), CliError> { //function to monitor dropped packets metrics - println!("{} {}", "=====>".blue().bold(), "Connecting to cortexflow Client".white()); + println!( + "{} {}", + "=====>".blue().bold(), + "Connecting to cortexflow Client".white() + ); match connect_to_client().await { Ok(client) => { - println!("{} {}", "=====>".blue().bold(), "Connected to CortexFlow Client".green()); + println!( + "{} {}", + "=====>".blue().bold(), + "Connected to CortexFlow Client".green() + ); //send request to get dropped packets metrics match agent_api::requests::send_dropped_packets_request(client).await { Ok(response) => { @@ -242,7 +252,8 @@ pub async fn monitor_dropped_packets() -> Result<(), Error> { resp.metrics.len() ); for (i, metric) in resp.metrics.iter().enumerate() { - let converted_timestamp= convert_timestamp_to_date(metric.timestamp_us); + let converted_timestamp = + convert_timestamp_to_date(metric.timestamp_us); println!( "{} DroppedPackets[{}]\n TGID: {}\n Process: {}\n SK Drops: {}\n Socket Errors: {}\n Soft Errors: {}\n Backlog Length: {}\n Write Memory Queued: {}\n Receive Buffer Size: {}\n ACK Backlog: {}\n Timestamp: {} µs", "=====>".blue().bold(), @@ -262,30 +273,26 @@ pub async fn monitor_dropped_packets() -> Result<(), Error> { } } Err(e) => { - println!( - "{} {} {} {}", - "=====>".blue().bold(), - "An error occured".red(), - "Error:", - e - ); - return Err(e); + return Err(CliError::AgentError( + tonic_reflection::server::Error::InvalidFileDescriptorSet(e.to_string()), + )); } } } Err(e) => { - println!( - "{} {}", - "=====>".blue().bold(), - "Failed to connect to CortexFlow Client".red() - ); - return Err(e); + return Err(CliError::ClientError(kube::Error::Api(ErrorResponse { + status: "failed".to_string(), + message: "Failed to connect to kubernetes client".to_string(), + reason: e.to_string(), + code: 404, + }))); } } Ok(()) } -fn convert_timestamp_to_date(timestamp:u64)->String{ - let datetime = DateTime::from_timestamp_micros(timestamp as i64).unwrap(); - datetime.to_string() +fn convert_timestamp_to_date(timestamp: u64) -> String { + DateTime::from_timestamp_micros(timestamp as i64) + .map(|dt| dt.to_string()) + .unwrap_or_else(|| "Cannot convert timestamp to date".to_string()) } diff --git a/cli/src/service.rs b/cli/src/service.rs index b66ed7e..8cfebf1 100644 --- a/cli/src/service.rs +++ b/cli/src/service.rs @@ -1,19 +1,22 @@ -use std::{ str, process::Command }; +use clap::{Args, Subcommand}; use colored::Colorize; -use clap::{ Args, Subcommand }; -use kube::{ core::ErrorResponse, Error }; +use kube::{Error, core::ErrorResponse}; +use std::{process::Command, str}; -use crate::essential::{ BASE_COMMAND, connect_to_client, CliError }; -use crate::logs::{ get_available_namespaces, check_namespace_exists }; +use crate::errors::CliError; +use crate::essential::{BASE_COMMAND, connect_to_client}; +use crate::logs::{check_namespace_exists, get_available_namespaces}; //service subcommands #[derive(Subcommand, Debug, Clone)] pub enum ServiceCommands { - #[command(name = "list", about = "Check services list")] List { + #[command(name = "list", about = "Check services list")] + List { #[arg(long)] namespace: Option, }, - #[command(name = "describe", about = "Describe service")] Describe { + #[command(name = "describe", about = "Describe service")] + Describe { service_name: String, #[arg(long)] namespace: Option, @@ -44,7 +47,12 @@ pub async fn list_services(namespace: Option) -> Result<(), CliError> { Ok(_) => { let ns = namespace.unwrap_or_else(|| "cortexflow".to_string()); - println!("{} {} {}", "=====>".blue().bold(), "Listing services in namespace:", ns); + println!( + "{} {} {}", + "=====>".blue().bold(), + "Listing services in namespace:", + ns + ); // Check if namespace exists first if !check_namespace_exists(&ns).await? { @@ -72,7 +80,9 @@ pub async fn list_services(namespace: Option) -> Result<(), CliError> { Ok(output) => { if !output.status.success() { let error = str::from_utf8(&output.stderr).unwrap_or("Unknown error"); - eprintln!("Error executing {}: {}", BASE_COMMAND, error); + return Err(CliError::BaseError { + reason: format!("Error executing {}: {}", BASE_COMMAND, error), + }); } let stdout = str::from_utf8(&output.stdout).unwrap_or(""); @@ -87,7 +97,10 @@ pub async fn list_services(namespace: Option) -> Result<(), CliError> { } // header for Table - println!("{:<40} {:<20} {:<10} {:<10}", "NAME", "STATUS", "RESTARTS", "AGE"); + println!( + "{:<40} {:<20} {:<10} {:<10}", + "NAME", "STATUS", "RESTARTS", "AGE" + ); println!("{}", "-".repeat(80)); // Display Each Pod. @@ -108,40 +121,33 @@ pub async fn list_services(namespace: Option) -> Result<(), CliError> { println!( "{:<40} {:<20} {:<10} {:<10}", - name, - full_status, - restarts, - age + name, full_status, restarts, age ); } } Ok(()) } Err(err) => { - Err( - CliError::ClientError( - Error::Api(ErrorResponse { - status: "failed".to_string(), - message: "Failed to execute the kubectl command".to_string(), - reason: "Your cluster is probably disconnected".to_string(), - code: 404, - }) - ) - ) + return { + Err(CliError::ClientError(Error::Api(ErrorResponse { + status: "failed".to_string(), + message: "Failed to execute the kubectl command".to_string(), + reason: err.to_string(), + code: 404, + }))) + }; } } } - Err(_) => { - Err( - CliError::ClientError( - Error::Api(ErrorResponse { - status: "failed".to_string(), - message: "Failed to connect to kubernetes client".to_string(), - reason: "Your cluster is probably disconnected".to_string(), - code: 404, - }) - ) - ) + Err(e) => { + return { + Err(CliError::ClientError(Error::Api(ErrorResponse { + status: "failed".to_string(), + message: "Failed to connect to kubernetes client".to_string(), + reason: e.to_string(), + code: 404, + }))) + }; } } } @@ -157,19 +163,19 @@ pub async fn list_services(namespace: Option) -> Result<(), CliError> { // - else return an empty Vector // // -// Returns a CliError if the connection fails +// Returns a CliError if the connection failsss pub async fn describe_service( service_name: String, - namespace: &Option + namespace: &Option, ) -> Result<(), CliError> { match connect_to_client().await { Ok(_) => { match list_services(namespace.clone()).await { Ok(_) => { - //let file_path = get_config_directory().unwrap().1; - - let ns = namespace.clone().unwrap_or_else(|| "cortexflow".to_string()); + let ns = namespace + .clone() + .unwrap_or_else(|| "cortexflow".to_string()); println!( "{} {} {} {} {}", @@ -193,7 +199,10 @@ pub async fn describe_service( for available_ns in &available_namespaces { println!(" • {}", available_ns); } - println!("\nTry: cortex service describe {} --namespace ", service_name); + println!( + "\nTry: cortexflow service describe {} --namespace ", + service_name + ); } else { println!("No namespaces found in the cluster."); } @@ -207,15 +216,14 @@ pub async fn describe_service( match output { Ok(output) => { if !output.status.success() { - let error = str - ::from_utf8(&output.stderr) - .unwrap_or("Unknown error"); - eprintln!("Error executing kubectl describe: {}", error); - eprintln!( - "Make sure the pod '{}' exists in namespace '{}'", - service_name, - ns - ); + let error = + str::from_utf8(&output.stderr).unwrap_or("Unknown error"); + return Err(CliError::BaseError { + reason: format!( + "Error executing kubectl describe: {}.Make sure the pod '{}' exists in namespace '{}'", + error, service_name, ns + ), + }); } let stdout = str::from_utf8(&output.stdout).unwrap_or(""); @@ -229,33 +237,33 @@ pub async fn describe_service( Ok(()) } Err(err) => { - Err( - CliError::ClientError( - Error::Api(ErrorResponse { - status: "failed".to_string(), - message: "Failed to execute the kubectl command ".to_string(), - reason: "Your cluster is probably disconnected".to_string(), - code: 404, - }) - ) - ) + return { + Err(CliError::ClientError(Error::Api(ErrorResponse { + status: "failed".to_string(), + message: "Failed to execute the kubectl command ".to_string(), + reason: err.to_string(), + code: 404, + }))) + }; } } } - Err(e) => todo!(), + Err(e) => { + return Err(CliError::BaseError { + reason: format!("Cannot list services: {}", e), + }); + } } } - Err(_) => { - Err( - CliError::ClientError( - Error::Api(ErrorResponse { - status: "failed".to_string(), - message: "Failed to connect to kubernetes client".to_string(), - reason: "Your cluster is probably disconnected".to_string(), - code: 404, - }) - ) - ) + Err(e) => { + return { + Err(CliError::ClientError(Error::Api(ErrorResponse { + status: "failed".to_string(), + message: "Failed to connect to kubernetes client".to_string(), + reason: e.to_string(), + code: 404, + }))) + }; } } } diff --git a/cli/src/status.rs b/cli/src/status.rs index 2680781..ca5d43a 100644 --- a/cli/src/status.rs +++ b/cli/src/status.rs @@ -4,7 +4,8 @@ use clap::Args; use kube::{ Error, core::ErrorResponse }; use crate::logs::{ get_available_namespaces, check_namespace_exists }; -use crate::essential::{ BASE_COMMAND, connect_to_client, CliError }; +use crate::essential::{ BASE_COMMAND, connect_to_client }; +use crate::errors::CliError; #[derive(Debug)] pub enum OutputFormat { @@ -130,13 +131,13 @@ pub async fn status_command( } } } - Err(_) => { - Err( + Err(e) => { + return Err( CliError::ClientError( Error::Api(ErrorResponse { status: "failed".to_string(), message: "Failed to connect to kubernetes client".to_string(), - reason: "Your cluster is probably disconnected".to_string(), + reason: e.to_string(), code: 404, }) ) @@ -185,13 +186,13 @@ async fn get_pods_status(namespace: &str) -> Result Ok(Vec::new()), } } - Err(_) => { - Err( + Err(e) => { + return Err( CliError::ClientError( Error::Api(ErrorResponse { status: "failed".to_string(), message: "Failed to connect to kubernetes client".to_string(), - reason: "Your cluster is probably disconnected".to_string(), + reason: e.to_string(), code: 404, }) ) @@ -240,13 +241,13 @@ async fn get_services_status(namespace: &str) -> Result Ok(Vec::new()), } } - Err(_) => { - Err( + Err(e) => { + return Err( CliError::ClientError( Error::Api(ErrorResponse { status: "failed".to_string(), message: "Failed to connect to kubernetes client".to_string(), - reason: "Your cluster is probably disconnected".to_string(), + reason: e.to_string(), code: 404, }) ) diff --git a/cli/src/uninstall.rs b/cli/src/uninstall.rs index 0d71cfa..b9558dd 100644 --- a/cli/src/uninstall.rs +++ b/cli/src/uninstall.rs @@ -1,7 +1,8 @@ use colored::Colorize; -use std::{io::stdin, process::Command, thread, time::Duration}; +use std::{io::stdin, process::Command}; -use crate::essential::{BASE_COMMAND, CliError, connect_to_client}; +use crate::errors::CliError; +use crate::essential::{BASE_COMMAND, connect_to_client}; use kube::{Error, core::ErrorResponse}; //docs: @@ -38,12 +39,16 @@ pub async fn uninstall() -> Result<(), CliError> { } Ok(()) } - Err(_) => Err(CliError::ClientError(Error::Api(ErrorResponse { - status: "failed".to_string(), - message: "Failed to connect to kubernetes client".to_string(), - reason: "Your cluster is probably disconnected".to_string(), - code: 404, - }))), + Err(e) => { + return { + Err(CliError::ClientError(Error::Api(ErrorResponse { + status: "failed".to_string(), + message: "Failed to connect to kubernetes client".to_string(), + reason: e.to_string(), + code: 404, + }))) + }; + } } } @@ -85,18 +90,21 @@ async fn uninstall_all() -> Result<(), CliError> { Ok(()) } else { let stderr = String::from_utf8_lossy(&output.stderr); - eprintln!("Error deleting cortexflow namespace. Error: {} ", stderr); - Err(CliError::InstallerError { + return Err(CliError::InstallerError { reason: format!("Failed to delete cortexflow namespace. Error: {}", stderr), - }) + }); } } - Err(_) => Err(CliError::ClientError(Error::Api(ErrorResponse { - status: "failed".to_string(), - message: "Failed to connect to kubernetes client".to_string(), - reason: "Your cluster is probably disconnected".to_string(), - code: 404, - }))), + Err(e) => { + return { + Err(CliError::ClientError(Error::Api(ErrorResponse { + status: "failed".to_string(), + message: "Failed to connect to kubernetes client".to_string(), + reason: e.to_string(), + code: 404, + }))) + }; + } } } @@ -131,44 +139,20 @@ async fn uninstall_component(component_type: &str, component: &str) -> Result<() Ok(()) } else { let stderr = String::from_utf8_lossy(&output.stderr); - eprintln!("Error deleting {}:\n{}", component, stderr); - Err(CliError::InstallerError { + return Err(CliError::InstallerError { reason: format!("Failed to delete component '{}': {}", component, stderr), - }) + }); } } - Err(_) => Err(CliError::ClientError(Error::Api(ErrorResponse { - status: "failed".to_string(), - message: "Failed to connect to kubernetes client".to_string(), - reason: "Your cluster is probably disconnected".to_string(), - code: 404, - }))), - } -} - -// -// -//docs: -// -// This function is deprecated and will be removed in the next version -// -// Do not include or refactor this function -#[deprecated(since = "0.1.4")] -fn rm_dir(directory_to_remove: &str) { - let output = Command::new("rm") - .args(["-rf", directory_to_remove]) - .output() - .expect("cannot remove directory"); - - if !output.status.success() { - eprintln!( - "Error removing directory: {}:\n{}", - directory_to_remove, - String::from_utf8_lossy(&output.stderr) - ); - } else { - println!("✅ Removed directory {}", directory_to_remove); + Err(e) => { + return { + Err(CliError::ClientError(Error::Api(ErrorResponse { + status: "failed".to_string(), + message: "Failed to connect to kubernetes client".to_string(), + reason: e.to_string(), + code: 404, + }))) + }; + } } - - thread::sleep(Duration::from_secs(2)); -} +} \ No newline at end of file diff --git a/core/src/testing/agent.yaml b/core/src/testing/agent.yaml index e5c54f0..d189f43 100644 --- a/core/src/testing/agent.yaml +++ b/core/src/testing/agent.yaml @@ -19,7 +19,7 @@ spec: hostNetwork: true containers: - name: agent - image: lorenzotettamanti/cortexflow-agent:latest + image: ghcr.io/cortexflow/agent:latest command: ["/bin/bash", "-c"] args: - | diff --git a/core/src/testing/identity.yaml b/core/src/testing/identity.yaml index 44fc5b9..bb027d2 100644 --- a/core/src/testing/identity.yaml +++ b/core/src/testing/identity.yaml @@ -53,7 +53,7 @@ spec: - SYS_PTRACE containers: - name: identity - image: lorenzotettamanti/cortexflow-identity:latest + image: ghcr.io/cortexflow/identity:latest command: ["/bin/bash", "-c"] args: - | diff --git a/core/src/testing/metrics.yaml b/core/src/testing/metrics.yaml index 3f74c71..4c775ca 100644 --- a/core/src/testing/metrics.yaml +++ b/core/src/testing/metrics.yaml @@ -19,7 +19,7 @@ spec: hostNetwork: true containers: - name: metrics - image: lorenzotettamanti/cortexflow-metrics:latest + image: ghcr.io/cortexflow/metrics:latest command: ["/bin/bash", "-c"] args: - |