From bdf9ea79b4139bb861beee47f53d236c42900c12 Mon Sep 17 00:00:00 2001 From: LorenzoTettamanti Date: Mon, 12 Jan 2026 18:27:30 +0100 Subject: [PATCH 01/10] [#171]: updated cli error handling. Added CliError to every function that returns a Result type --- cli/src/essential.rs | 53 +++++++++---- cli/src/install.rs | 46 ++++++----- cli/src/logs.rs | 36 ++++----- cli/src/main.rs | 1 - cli/src/monitoring.rs | 174 ++++++++++++++++++++++-------------------- cli/src/service.rs | 128 ++++++++++++++++--------------- cli/src/status.rs | 18 ++--- cli/src/uninstall.rs | 58 ++++++++------ 8 files changed, 283 insertions(+), 231 deletions(-) diff --git a/cli/src/essential.rs b/cli/src/essential.rs index 3f43350..1512c80 100644 --- a/cli/src/essential.rs +++ b/cli/src/essential.rs @@ -1,12 +1,10 @@ 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 anyhow::Error; use colored::Colorize; -use k8s_openapi::apimachinery::pkg::version; use kube::core::ErrorResponse; use serde::Serialize; @@ -69,10 +67,20 @@ impl From for CliError { } } impl From<()> for CliError { - fn from(v: ()) -> Self { + fn from(e: ()) -> Self { return ().into(); } } +impl From for CliError { + fn from(e: prost::DecodeError) -> Self { + todo!() + } +} +impl From for CliError { + fn from(e: tonic::Status) -> Self { + todo!() + } +} // docs: // fmt::Display implementation for CliError type. Creates a user friendly message error message. @@ -84,7 +92,11 @@ impl fmt::Display for CliError { CliError::InstallerError { reason } => { write!( f, - "An error occured while installing cortexflow components. Reason: {}", + "{} {} {}", + "=====>".blue().bold(), + "An error occured while installing cortexflow components. Reason:" + .bold() + .red(), reason ) } @@ -103,7 +115,15 @@ impl fmt::Display for CliError { ) } CliError::ClientError(e) => write!(f, "Client Error: {}", e), - CliError::AgentError(e) => write!(f, "Agent Error: {}", e), + CliError::AgentError(e) => { + write!( + f, + "{} {} {}", + "=====>".bold().blue(), + "Agent Error:".bold().red(), + e + ) + } } } } @@ -217,16 +237,17 @@ pub fn update_cli() { // docs: // // This function returns the latest version of the CLI from the crates.io registry -pub fn get_latest_cfcli_version() -> Result { +// TODO: implement CliError here +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 +344,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 +372,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); @@ -378,10 +399,10 @@ pub async fn create_config_file(config_struct: MetadataConfigFile) -> Result<(), } 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, }))), } @@ -479,10 +500,10 @@ pub async fn update_configmap(config_struct: MetadataConfigFile) -> Result<(), C 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..4dd3e12 100644 --- a/cli/src/install.rs +++ b/cli/src/install.rs @@ -1,7 +1,7 @@ use crate::essential::{ BASE_COMMAND, CliError, connect_to_client, create_config_file, create_configs, }; -use clap::{Args, Subcommand, command}; +use clap::{Args, Subcommand}; use colored::Colorize; use kube::Error; use kube::core::ErrorResponse; @@ -144,12 +144,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 +194,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, + }))) + }; + } } } @@ -282,8 +290,8 @@ 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() { @@ -366,8 +374,8 @@ 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() { @@ -396,8 +404,8 @@ 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() { diff --git a/cli/src/logs.rs b/cli/src/logs.rs index bd819cc..1efd9bc 100644 --- a/cli/src/logs.rs +++ b/cli/src/logs.rs @@ -146,13 +146,13 @@ pub async fn logs_command( Ok(()) } - 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, }) ) @@ -181,13 +181,13 @@ pub async fn check_namespace_exists(namespace: &str) -> Result { Err(_) => Ok(false), } } - 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, }) ) @@ -232,13 +232,13 @@ pub async fn get_available_namespaces() -> Result, CliError> { _ => 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, }) ) @@ -290,13 +290,13 @@ async fn get_pods_for_service( _ => 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, }) ) @@ -349,13 +349,13 @@ async fn get_pods_for_component( _ => 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, }) ) @@ -402,13 +402,13 @@ async fn get_all_pods(namespace: &str) -> Result, CliError> { _ => 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/main.rs b/cli/src/main.rs index 272123f..dea5d83 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,4 +1,3 @@ -#![allow(warnings)] mod essential; mod install; mod logs; diff --git a/cli/src/monitoring.rs b/cli/src/monitoring.rs index 506cc5f..1be9e31 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::essential::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::ClientError(kube::Error::Api(ErrorResponse { + status: "failed".to_string(), + message: "Failed to connect to kubernetes client".to_string(), + reason: e.to_string(), + code: 404, + }))); } } } Err(e) => { - println!( - "{} {}", - "=====>".blue().bold(), - "Failed to connect to CortexFlow Server Reflection".red() - ); - return Err(e); + return Err(CliError::AgentError( + tonic_reflection::server::Error::InvalidFileDescriptorSet(e.to_string()), + )); } } 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::ClientError(kube::Error::Api(ErrorResponse { + status: "failed".to_string(), + message: "Failed to connect to kubernetes client".to_string(), + reason: e.to_string(), + code: 404, + }))); } } } Err(e) => { - println!( - "{} {}", - "=====>".blue().bold(), - "Failed to connect to CortexFlow Client".red() - ); - return Err(e); + return Err(CliError::AgentError( + tonic_reflection::server::Error::InvalidFileDescriptorSet(e.to_string()), + )); } } 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,42 @@ 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::ClientError(kube::Error::Api(ErrorResponse { + status: "failed".to_string(), + message: "Failed to connect to kubernetes client".to_string(), + reason: e.to_string(), + code: 404, + }))); } } } 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 +255,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 +276,28 @@ 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::ClientError(kube::Error::Api(ErrorResponse { + status: "failed".to_string(), + message: "Failed to connect to kubernetes client".to_string(), + reason: e.to_string(), + code: 404, + }))); } } } 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{ +fn convert_timestamp_to_date(timestamp: u64) -> String { let datetime = DateTime::from_timestamp_micros(timestamp as i64).unwrap(); datetime.to_string() } diff --git a/cli/src/service.rs b/cli/src/service.rs index b66ed7e..91e6ee0 100644 --- a/cli/src/service.rs +++ b/cli/src/service.rs @@ -1,19 +1,21 @@ -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::essential::{BASE_COMMAND, CliError, 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 +46,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? { @@ -87,7 +94,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 +118,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, + }))) + }; } } } @@ -161,7 +164,7 @@ pub async fn list_services(namespace: Option) -> Result<(), CliError> { pub async fn describe_service( service_name: String, - namespace: &Option + namespace: &Option, ) -> Result<(), CliError> { match connect_to_client().await { Ok(_) => { @@ -169,7 +172,9 @@ pub async fn describe_service( 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 +198,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: cortex service describe {} --namespace ", + service_name + ); } else { println!("No namespaces found in the cluster."); } @@ -207,14 +215,12 @@ pub async fn describe_service( match output { Ok(output) => { if !output.status.success() { - let error = str - ::from_utf8(&output.stderr) - .unwrap_or("Unknown error"); + 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 + service_name, ns ); } @@ -229,33 +235,29 @@ 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(_) => { - 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..16570cc 100644 --- a/cli/src/status.rs +++ b/cli/src/status.rs @@ -130,13 +130,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 +185,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 +240,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..afcc935 100644 --- a/cli/src/uninstall.rs +++ b/cli/src/uninstall.rs @@ -38,12 +38,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 +89,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,18 +138,21 @@ 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, - }))), + 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, + }))) + }; + } } } From 0b4f1c41d766ba02223f434e845554697db0653d Mon Sep 17 00:00:00 2001 From: LorenzoTettamanti Date: Mon, 12 Jan 2026 18:40:14 +0100 Subject: [PATCH 02/10] [#171]: moved custom errors implementation in the error.rs module --- cli/src/errors.rs | 115 +++++++++++++++++++++++++++++ cli/src/essential.rs | 118 +----------------------------- cli/src/install.rs | 3 +- cli/src/logs.rs | 134 +++++++++++++++------------------- cli/src/main.rs | 166 ++++++++++++++++++++++++------------------ cli/src/mod.rs | 3 +- cli/src/monitoring.rs | 2 +- cli/src/service.rs | 3 +- cli/src/status.rs | 3 +- cli/src/uninstall.rs | 3 +- 10 files changed, 283 insertions(+), 267 deletions(-) create mode 100644 cli/src/errors.rs diff --git a/cli/src/errors.rs b/cli/src/errors.rs new file mode 100644 index 0000000..bf83d21 --- /dev/null +++ b/cli/src/errors.rs @@ -0,0 +1,115 @@ +use colored::Colorize; +use std::fmt; + +// 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(e: ()) -> Self { + return ().into(); + } +} +impl From for CliError { + fn from(e: prost::DecodeError) -> Self { + todo!() + } +} +impl From for CliError { + fn from(e: tonic::Status) -> Self { + todo!() + } +} + +// 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, + "{} {} {}", + "=====>".blue().bold(), + "An error occured while installing cortexflow components. Reason:" + .bold() + .red(), + 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, + "{} {} {}", + "=====>".bold().blue(), + "Agent Error:".bold().red(), + e + ) + } + } + } +} diff --git a/cli/src/essential.rs b/cli/src/essential.rs index 1512c80..4067db0 100644 --- a/cli/src/essential.rs +++ b/cli/src/essential.rs @@ -1,9 +1,9 @@ +use crate::errors::CliError; use std::borrow::Cow; 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 kube::core::ErrorResponse; use serde::Serialize; @@ -15,119 +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(e: ()) -> Self { - return ().into(); - } -} -impl From for CliError { - fn from(e: prost::DecodeError) -> Self { - todo!() - } -} -impl From for CliError { - fn from(e: tonic::Status) -> Self { - todo!() - } -} - -// 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, - "{} {} {}", - "=====>".blue().bold(), - "An error occured while installing cortexflow components. Reason:" - .bold() - .red(), - 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, - "{} {} {}", - "=====>".bold().blue(), - "Agent Error:".bold().red(), - e - ) - } - } - } -} - #[derive(Serialize)] pub struct MetadataConfigFile { blocklist: Vec, @@ -237,7 +124,6 @@ pub fn update_cli() { // docs: // // This function returns the latest version of the CLI from the crates.io registry -// TODO: implement CliError here pub fn get_latest_cfcli_version() -> Result { let output = Command::new("cargo") .args(["search", "cortexflow-cli", "--limit", "1"]) diff --git a/cli/src/install.rs b/cli/src/install.rs index 4dd3e12..19aacc9 100644 --- a/cli/src/install.rs +++ b/cli/src/install.rs @@ -1,6 +1,7 @@ use crate::essential::{ - BASE_COMMAND, CliError, connect_to_client, create_config_file, create_configs, + BASE_COMMAND, connect_to_client, create_config_file, create_configs, }; +use crate::errors::CliError; use clap::{Args, Subcommand}; use colored::Colorize; use kube::Error; diff --git a/cli/src/logs.rs b/cli/src/logs.rs index 1efd9bc..4a6ce3f 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() { @@ -136,9 +144,7 @@ pub async fn logs_command( Err(err) => { eprintln!( "Failed to execute {} logs for pod '{:?}': {}", - BASE_COMMAND, - pod, - err + BASE_COMMAND, pod, err ); } } @@ -147,16 +153,12 @@ pub async fn logs_command( Ok(()) } 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, - }) - ) - ) + 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,7 +176,9 @@ 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()), @@ -182,16 +186,12 @@ pub async fn check_namespace_exists(namespace: &str) -> Result { } } 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, - }) - ) - ) + 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, + }))); } } } @@ -233,16 +233,12 @@ pub async fn get_available_namespaces() -> Result, CliError> { } } 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, - }) - ) - ) + 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 +255,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(_) => { @@ -291,16 +287,12 @@ async fn get_pods_for_service( } } 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, - }) - ) - ) + 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 +310,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(_) => { @@ -350,16 +342,12 @@ async fn get_pods_for_component( } } 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, - }) - ) - ) + 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, + }))); } } } @@ -403,16 +391,12 @@ async fn get_all_pods(namespace: &str) -> Result, CliError> { } } 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, - }) - ) - ) + 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 dea5d83..ba6b623 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,3 +1,4 @@ +mod errors; mod essential; mod install; mod logs; @@ -7,24 +8,24 @@ mod service; mod status; mod uninstall; -use clap::{ Args, Parser, Subcommand }; +use clap::{Args, Parser, Subcommand}; use colored::Colorize; 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; @@ -45,18 +46,24 @@ struct Cli { #[derive(Subcommand, Debug, Clone)] 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)] struct SetArgs { @@ -67,17 +74,18 @@ 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.map_err(|e| eprintln!("{}", e))?; + } + InstallCommands::TestPods => { + install_simple_example() + .await + .map_err(|e| eprintln!("{}", e))?; } + }, Some(Commands::Uninstall) => { - uninstall().await.map_err(|e| eprintln!("{}",e) )?; + uninstall().await.map_err(|e| eprintln!("{}", e))?; } Some(Commands::Update) => { update_cli(); @@ -85,40 +93,55 @@ async fn args_parser() -> Result<(), CliError> { 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 + .map_err(|e| eprintln!("{}", e))?; + } + ServiceCommands::Describe { + service_name, + namespace, + } => { + describe_service(service_name, &namespace) + .await + .map_err(|e| eprintln!("{}", e))?; } + }, 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 + .map_err(|e| eprintln!("{}", e))?; } 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 + .map_err(|e| eprintln!("{}", e))?; } - 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.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::Policies(policies_args)) => { match policies_args.policy_cmd { PoliciesCommands::CheckBlocklist => { - let _ = check_blocklist().await.map_err(|e| eprintln!("{}",e) )?; + let _ = check_blocklist().await.map_err(|e| eprintln!("{}", e))?; } PoliciesCommands::CreateBlocklist => { // pass the ip as a monitoring flag @@ -132,7 +155,9 @@ 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 + .map_err(|e| eprintln!("{}", e))?; } Err(e) => { eprintln!("{}", e); @@ -141,26 +166,27 @@ async fn args_parser() -> Result<(), CliError> { } } } - 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 => { + 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); } } } + }, } } None => { 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 1be9e31..d6b120c 100644 --- a/cli/src/monitoring.rs +++ b/cli/src/monitoring.rs @@ -10,7 +10,7 @@ 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 crate::essential::CliError; +use crate::errors::CliError; use clap::{Args, Subcommand}; //monitoring subcommands diff --git a/cli/src/service.rs b/cli/src/service.rs index 91e6ee0..21e8e94 100644 --- a/cli/src/service.rs +++ b/cli/src/service.rs @@ -3,7 +3,8 @@ use colored::Colorize; use kube::{Error, core::ErrorResponse}; use std::{process::Command, str}; -use crate::essential::{BASE_COMMAND, CliError, connect_to_client}; +use crate::errors::CliError; +use crate::essential::{BASE_COMMAND, connect_to_client}; use crate::logs::{check_namespace_exists, get_available_namespaces}; //service subcommands diff --git a/cli/src/status.rs b/cli/src/status.rs index 16570cc..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 { diff --git a/cli/src/uninstall.rs b/cli/src/uninstall.rs index afcc935..e847ca2 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 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: From 1798067ebcccee448110567f3e4767653e8469eb Mon Sep 17 00:00:00 2001 From: LorenzoTettamanti Date: Mon, 12 Jan 2026 18:42:05 +0100 Subject: [PATCH 03/10] updated k8s manifests file: updated image repository from my personal repository to the cortexflow org ghcr.io repository --- core/src/testing/agent.yaml | 2 +- core/src/testing/identity.yaml | 2 +- core/src/testing/metrics.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) 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: - | From ecb9fc1f3c25812f4ee6ca3d2fedbedfce29b876 Mon Sep 17 00:00:00 2001 From: LorenzoTettamanti Date: Mon, 12 Jan 2026 21:07:12 +0100 Subject: [PATCH 04/10] [dependencies]: update CLI dependencies --- cli/Cargo.lock | 36 ++++++++++++++++++------------------ cli/Cargo.toml | 10 +++++----- 2 files changed, 23 insertions(+), 23 deletions(-) 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"]} From 8f77322fbdf7472b669293e8eb94d0821513ead2 Mon Sep 17 00:00:00 2001 From: LorenzoTettamanti Date: Mon, 12 Jan 2026 21:38:23 +0100 Subject: [PATCH 05/10] [#171]: removed deprecated rm_dir function --- cli/src/uninstall.rs | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/cli/src/uninstall.rs b/cli/src/uninstall.rs index e847ca2..5259d49 100644 --- a/cli/src/uninstall.rs +++ b/cli/src/uninstall.rs @@ -155,31 +155,4 @@ async fn uninstall_component(component_type: &str, component: &str) -> Result<() }; } } -} - -// -// -//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); - } - - thread::sleep(Duration::from_secs(2)); -} +} \ No newline at end of file From b13522867a6c70623d63a6a0c11a51aa87ed6e57 Mon Sep 17 00:00:00 2001 From: LorenzoTettamanti Date: Mon, 12 Jan 2026 22:53:38 +0100 Subject: [PATCH 06/10] [#171]: cleaned unused dependencies. Fixed error propagation from Result type with ? operator for the apply_component() function in install.rs module. Fixed stack overflow error while returning a custom error. implemented coloured error outputs,added docs in error.rs module --- cli/src/errors.rs | 53 +++++++++++++++++++++++--------------------- cli/src/install.rs | 4 ++-- cli/src/main.rs | 50 +++++++++++++---------------------------- cli/src/service.rs | 1 + cli/src/uninstall.rs | 2 +- 5 files changed, 47 insertions(+), 63 deletions(-) diff --git a/cli/src/errors.rs b/cli/src/errors.rs index bf83d21..6e37504 100644 --- a/cli/src/errors.rs +++ b/cli/src/errors.rs @@ -15,10 +15,6 @@ use std::fmt; // - 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 @@ -33,12 +29,15 @@ use std::fmt; pub enum CliError { InstallerError { reason: String }, ClientError(kube::Error), - UninstallError { reason: String }, AgentError(tonic_reflection::server::Error), MonitoringError { reason: String }, } // docs: -// error type conversions +// +// 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 { @@ -48,29 +47,28 @@ impl From for CliError { impl From for CliError { fn from(e: anyhow::Error) -> Self { CliError::MonitoringError { - reason: format!("{}", e), + reason: e.to_string(), } } } -impl From<()> for CliError { - fn from(e: ()) -> Self { - return ().into(); - } -} impl From for CliError { fn from(e: prost::DecodeError) -> Self { - todo!() + return CliError::AgentError(tonic_reflection::server::Error::DecodeError(e)); } } impl From for CliError { fn from(e: tonic::Status) -> Self { - todo!() + return CliError::MonitoringError { + reason: e.to_string(), + }; } } // 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 +// +// 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 { @@ -83,31 +81,36 @@ impl fmt::Display for CliError { "An error occured while installing cortexflow components. Reason:" .bold() .red(), - reason + reason.red().bold() ) } - CliError::UninstallError { reason } => { + CliError::MonitoringError { reason } => { write!( f, - "An error occured while installing cortexflow components. Reason: {}", - reason + "{} {} {}", + "=====>".blue().bold(), + "An error occured while installing cortexflow components. Reason:" + .bold() + .red(), + reason.red().bold() ) } - CliError::MonitoringError { reason } => { + CliError::ClientError(e) => { write!( f, - "An error occured while installing cortexflow components. Reason: {}", - reason + "{} {} {}", + "=====>".blue().bold(), + "Client Error:".bold().red(), + e.to_string().red().bold() ) } - CliError::ClientError(e) => write!(f, "Client Error: {}", e), CliError::AgentError(e) => { write!( f, "{} {} {}", "=====>".bold().blue(), "Agent Error:".bold().red(), - e + e.to_string().bold().red() ) } } diff --git a/cli/src/install.rs b/cli/src/install.rs index 19aacc9..9ec4ecb 100644 --- a/cli/src/install.rs +++ b/cli/src/install.rs @@ -247,7 +247,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" { @@ -267,7 +267,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 { diff --git a/cli/src/main.rs b/cli/src/main.rs index ba6b623..edf820b 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -76,16 +76,14 @@ async fn args_parser() -> Result<(), CliError> { match args.cmd { Some(Commands::Install(installation_args)) => match installation_args.install_cmd { InstallCommands::All => { - install_cortexflow().await.map_err(|e| eprintln!("{}", e))?; + install_cortexflow().await?; } InstallCommands::TestPods => { - install_simple_example() - .await - .map_err(|e| eprintln!("{}", e))?; + install_simple_example().await?; } }, Some(Commands::Uninstall) => { - uninstall().await.map_err(|e| eprintln!("{}", e))?; + uninstall().await?; } Some(Commands::Update) => { update_cli(); @@ -95,53 +93,39 @@ async fn args_parser() -> Result<(), CliError> { } Some(Commands::Service(service_args)) => match service_args.service_cmd { ServiceCommands::List { namespace } => { - list_services(namespace) - .await - .map_err(|e| eprintln!("{}", e))?; + list_services(namespace).await?; } ServiceCommands::Describe { service_name, namespace, } => { - describe_service(service_name, &namespace) - .await - .map_err(|e| eprintln!("{}", e))?; + 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))?; + let _ = list_features().await?; } MonitorCommands::Connections => { - let _ = monitor_identity_events() - .await - .map_err(|e| eprintln!("{}", e))?; + let _ = monitor_identity_events().await?; } MonitorCommands::Latencymetrics => { - let _ = monitor_latency_metrics() - .await - .map_err(|e| eprintln!("{}", e))?; + let _ = monitor_latency_metrics().await?; } MonitorCommands::Droppedpackets => { - let _ = monitor_dropped_packets() - .await - .map_err(|e| eprintln!("{}", e))?; + 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 @@ -155,9 +139,7 @@ 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); @@ -177,9 +159,7 @@ async fn args_parser() -> Result<(), CliError> { println!("Inserted ip: {}", ip); match remove_ip(&ip).await { Ok(_) => { - let _ = update_config_metadata(&ip, "delete") - .await - .map_err(|e| eprintln!("{}", e))?; + let _ = update_config_metadata(&ip, "delete").await?; } Err(e) => { eprintln!("{}", e); @@ -198,5 +178,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/service.rs b/cli/src/service.rs index 21e8e94..37beb58 100644 --- a/cli/src/service.rs +++ b/cli/src/service.rs @@ -1,5 +1,6 @@ use clap::{Args, Subcommand}; use colored::Colorize; +use kube::Client; use kube::{Error, core::ErrorResponse}; use std::{process::Command, str}; diff --git a/cli/src/uninstall.rs b/cli/src/uninstall.rs index 5259d49..b9558dd 100644 --- a/cli/src/uninstall.rs +++ b/cli/src/uninstall.rs @@ -1,5 +1,5 @@ use colored::Colorize; -use std::{io::stdin, process::Command, thread, time::Duration}; +use std::{io::stdin, process::Command}; use crate::errors::CliError; use crate::essential::{BASE_COMMAND, connect_to_client}; From e72b6157f7757d4bfbf3a14014e69a1a00660d74 Mon Sep 17 00:00:00 2001 From: LorenzoTettamanti Date: Tue, 13 Jan 2026 20:31:22 +0100 Subject: [PATCH 07/10] [#171]: fixed error logic --- cli/src/monitoring.rs | 54 +++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/cli/src/monitoring.rs b/cli/src/monitoring.rs index d6b120c..39a56ce 100644 --- a/cli/src/monitoring.rs +++ b/cli/src/monitoring.rs @@ -77,19 +77,19 @@ pub async fn list_features() -> Result<(), CliError> { } } 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, - }))); + return Err(CliError::AgentError( + tonic_reflection::server::Error::InvalidFileDescriptorSet(e.to_string()), + )); } } } Err(e) => { - return Err(CliError::AgentError( - tonic_reflection::server::Error::InvalidFileDescriptorSet(e.to_string()), - )); + 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(()) @@ -133,19 +133,19 @@ pub async fn monitor_identity_events() -> Result<(), CliError> { } } 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, - }))); + return Err(CliError::AgentError( + tonic_reflection::server::Error::InvalidFileDescriptorSet(e.to_string()), + )); } } } Err(e) => { - return Err(CliError::AgentError( - tonic_reflection::server::Error::InvalidFileDescriptorSet(e.to_string()), - )); + 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, + }))); } } @@ -203,12 +203,9 @@ pub async fn monitor_latency_metrics() -> Result<(), CliError> { } } 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, - }))); + return Err(CliError::AgentError( + tonic_reflection::server::Error::InvalidFileDescriptorSet(e.to_string()), + )); } } } @@ -276,12 +273,9 @@ pub async fn monitor_dropped_packets() -> Result<(), CliError> { } } 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, - }))); + return Err(CliError::AgentError( + tonic_reflection::server::Error::InvalidFileDescriptorSet(e.to_string()), + )); } } } From 7ccc1f70b572643dc44f67c86ec1f0f3628749bc Mon Sep 17 00:00:00 2001 From: LorenzoTettamanti Date: Tue, 13 Jan 2026 21:51:56 +0100 Subject: [PATCH 08/10] [#171]: removed unused imports, Added CliError::BaseError to return general errors. simplified ClientError responses and AgentError responses --- cli/src/errors.rs | 26 ++++++++++++++++---------- cli/src/essential.rs | 39 ++++++++++++++++++++++++++------------- cli/src/install.rs | 40 ++++++++++++++++++++++------------------ cli/src/logs.rs | 17 ++++++++++++----- cli/src/main.rs | 2 +- cli/src/monitoring.rs | 5 +++-- cli/src/service.rs | 28 ++++++++++++++++------------ 7 files changed, 96 insertions(+), 61 deletions(-) diff --git a/cli/src/errors.rs b/cli/src/errors.rs index 6e37504..b813e35 100644 --- a/cli/src/errors.rs +++ b/cli/src/errors.rs @@ -1,11 +1,15 @@ use colored::Colorize; -use std::fmt; +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 @@ -20,8 +24,6 @@ use std::fmt; // - 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 @@ -30,7 +32,7 @@ pub enum CliError { InstallerError { reason: String }, ClientError(kube::Error), AgentError(tonic_reflection::server::Error), - MonitoringError { reason: String }, + BaseError { reason: String }, } // docs: // @@ -46,7 +48,7 @@ impl From for CliError { } impl From for CliError { fn from(e: anyhow::Error) -> Self { - CliError::MonitoringError { + CliError::BaseError { reason: e.to_string(), } } @@ -58,7 +60,7 @@ impl From for CliError { } impl From for CliError { fn from(e: tonic::Status) -> Self { - return CliError::MonitoringError { + return CliError::BaseError { reason: e.to_string(), }; } @@ -84,33 +86,37 @@ impl fmt::Display for CliError { reason.red().bold() ) } - CliError::MonitoringError { reason } => { + CliError::BaseError { reason } => { write!( f, "{} {} {}", "=====>".blue().bold(), - "An error occured while installing cortexflow components. Reason:" + "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(), - e.to_string().red().bold() + msg.to_string().red().bold() ) } CliError::AgentError(e) => { + let msg = Error::source(e).unwrap(); write!( f, "{} {} {}", "=====>".bold().blue(), "Agent Error:".bold().red(), - e.to_string().bold().red() + msg.to_string().bold().red() ) } } diff --git a/cli/src/essential.rs b/cli/src/essential.rs index 4067db0..5ca01b9 100644 --- a/cli/src/essential.rs +++ b/cli/src/essential.rs @@ -50,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!( @@ -65,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) @@ -106,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!( "{} {}", @@ -119,6 +123,7 @@ pub fn update_cli() { } } } + Ok(()) } // docs: @@ -280,7 +285,12 @@ 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(()) @@ -329,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 @@ -379,8 +391,9 @@ 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), + }); } } diff --git a/cli/src/install.rs b/cli/src/install.rs index 9ec4ecb..0045ef7 100644 --- a/cli/src/install.rs +++ b/cli/src/install.rs @@ -1,7 +1,5 @@ -use crate::essential::{ - BASE_COMMAND, connect_to_client, create_config_file, create_configs, -}; 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; @@ -296,11 +294,13 @@ fn apply_component(file: &str) -> Result<(), CliError> { })?; 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); } @@ -380,11 +380,13 @@ fn download_file(src: &str) -> Result<(), CliError> { })?; 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); } @@ -410,11 +412,13 @@ fn rm_file(file_to_remove: &str) -> Result<(), CliError> { })?; 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 4a6ce3f..102d97b 100644 --- a/cli/src/logs.rs +++ b/cli/src/logs.rs @@ -138,14 +138,21 @@ 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 + ), + }); } } } diff --git a/cli/src/main.rs b/cli/src/main.rs index edf820b..da80680 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -86,7 +86,7 @@ async fn args_parser() -> Result<(), CliError> { uninstall().await?; } Some(Commands::Update) => { - update_cli(); + update_cli()?; } Some(Commands::Info) => { info(); diff --git a/cli/src/monitoring.rs b/cli/src/monitoring.rs index 39a56ce..b7cf3e2 100644 --- a/cli/src/monitoring.rs +++ b/cli/src/monitoring.rs @@ -292,6 +292,7 @@ pub async fn monitor_dropped_packets() -> Result<(), CliError> { } fn convert_timestamp_to_date(timestamp: u64) -> String { - let datetime = DateTime::from_timestamp_micros(timestamp as i64).unwrap(); - datetime.to_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 37beb58..8cfebf1 100644 --- a/cli/src/service.rs +++ b/cli/src/service.rs @@ -1,6 +1,5 @@ use clap::{Args, Subcommand}; use colored::Colorize; -use kube::Client; use kube::{Error, core::ErrorResponse}; use std::{process::Command, str}; @@ -81,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(""); @@ -162,7 +163,7 @@ 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, @@ -172,8 +173,6 @@ pub async fn describe_service( 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()); @@ -201,7 +200,7 @@ pub async fn describe_service( println!(" • {}", available_ns); } println!( - "\nTry: cortex service describe {} --namespace ", + "\nTry: cortexflow service describe {} --namespace ", service_name ); } else { @@ -219,11 +218,12 @@ pub async fn describe_service( 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 - ); + 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(""); @@ -248,7 +248,11 @@ pub async fn describe_service( } } } - Err(e) => todo!(), + Err(e) => { + return Err(CliError::BaseError { + reason: format!("Cannot list services: {}", e), + }); + } } } Err(e) => { From 44e3d89f1a235a6bf9ec0806a5a8aa42d9e90efa Mon Sep 17 00:00:00 2001 From: LorenzoTettamanti Date: Tue, 13 Jan 2026 22:31:05 +0100 Subject: [PATCH 09/10] [#171]: improved main.rs error structure --- cli/src/main.rs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index da80680..7ef8872 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -9,7 +9,6 @@ mod status; mod uninstall; use clap::{Args, Parser, Subcommand}; -use colored::Colorize; use std::result::Result::Ok; use tracing::debug; @@ -131,7 +130,9 @@ async fn args_parser() -> Result<(), CliError> { // 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); @@ -142,7 +143,9 @@ async fn args_parser() -> Result<(), CliError> { let _ = update_config_metadata(&ip, "add").await?; } Err(e) => { - eprintln!("{}", e); + return Err(CliError::BaseError { + reason: e.to_string(), + }); } } } @@ -150,10 +153,10 @@ async fn args_parser() -> Result<(), CliError> { } PoliciesCommands::RemoveIpFromBlocklist => match policies_args.flags { None => { - eprintln!( - "{}", - "Insert at least one ip to remove from the blocklist".red() - ); + return Err(CliError::BaseError { + reason: "Insert at least one ip to remove from the blocklist" + .to_string(), + }); } Some(ip) => { println!("Inserted ip: {}", ip); @@ -162,7 +165,9 @@ async fn args_parser() -> Result<(), CliError> { let _ = update_config_metadata(&ip, "delete").await?; } Err(e) => { - eprintln!("{}", e); + return Err(CliError::BaseError { + reason: e.to_string(), + }); } } } @@ -170,7 +175,9 @@ async fn args_parser() -> Result<(), CliError> { } } 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(()) From f1a0345170b34d91c6f4d7ced6cb7668eff38ba0 Mon Sep 17 00:00:00 2001 From: LorenzoTettamanti Date: Tue, 13 Jan 2026 22:36:01 +0100 Subject: [PATCH 10/10] [#171]: removed useless derive macros --- cli/src/install.rs | 2 +- cli/src/main.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/src/install.rs b/cli/src/install.rs index 0045ef7..bdb1ea1 100644 --- a/cli/src/install.rs +++ b/cli/src/install.rs @@ -41,7 +41,7 @@ pub enum InstallCommands { } //install args -#[derive(Args, Debug, Clone)] +#[derive(Args, Debug)] pub struct InstallArgs { #[command(subcommand)] pub install_cmd: InstallCommands, diff --git a/cli/src/main.rs b/cli/src/main.rs index 7ef8872..0a5ac46 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -42,7 +42,7 @@ struct Cli { cmd: Option, } -#[derive(Subcommand, Debug, Clone)] +#[derive(Subcommand, Debug)] enum Commands { /* list of available commands */ #[command(name = "install", about = "Manage installation")] @@ -64,7 +64,7 @@ enum Commands { #[command(name = "policy", about = "Network Policies")] Policies(PoliciesArgs), } -#[derive(Args, Debug, Clone)] +#[derive(Args)] struct SetArgs { val: String, }