diff --git a/bin/src/cli.rs b/bin/src/cli.rs index 454dc9dbd..28497a978 100644 --- a/bin/src/cli.rs +++ b/bin/src/cli.rs @@ -24,6 +24,13 @@ pub struct Args { help = "Sets a custom timeout for commands (in milliseconds). 0 disables the timeout" )] pub timeout: Option, + #[clap( + short = 'j', + long = "json", + global = true, + help = "display responses to queries in a JSON format" + )] + pub json: bool, #[clap(subcommand)] pub cmd: SubCmd, } @@ -109,26 +116,12 @@ pub enum SubCmd { }, #[clap(name = "status", about = "gets information on the running workers")] - Status { - #[clap( - short = 'j', - long = "json", - help = "Print the command result in JSON format" - )] - json: bool, - }, + Status, #[clap( name = "metrics", about = "gets statistics on the main process and its workers" )] Metrics { - #[clap( - short = 'j', - long = "json", - help = "Print the command result in JSON format", - global = true - )] - json: bool, #[clap(subcommand)] cmd: MetricsCmd, }, @@ -153,22 +146,9 @@ pub enum SubCmd { help = "use a different configuration file from the current one" )] file: Option, - #[clap( - short = 'j', - long = "json", - help = "Print the command result in JSON format" - )] - json: bool, }, #[clap(name = "cluster", about = "cluster management")] Cluster { - #[clap( - short = 'j', - long = "json", - help = "Print the command result in JSON format", - global = true - )] - json: bool, #[clap(subcommand)] cmd: ClusterCmd, }, @@ -189,13 +169,6 @@ pub enum SubCmd { }, #[clap(name = "certificate", about = "list, add and remove certificates")] Certificate { - #[clap( - short = 'j', - long = "json", - help = "Print the command result in JSON format", - global = true - )] - json: bool, #[clap(subcommand)] cmd: CertificateCmd, }, @@ -216,7 +189,10 @@ pub enum MetricsCmd { Disable, #[clap(name = "clear", about = "Deletes local metrics data")] Clear, - #[clap(name = "get", about = "get all or filtered metrics")] + #[clap( + name = "get", + about = "get all metrics, filtered, or a list of available metrics" + )] Get { #[clap(short, long, help = "list the available metrics on the proxy level")] list: bool, diff --git a/bin/src/ctl/command.rs b/bin/src/ctl/command.rs index d291b1e3f..bf45c9178 100644 --- a/bin/src/ctl/command.rs +++ b/bin/src/ctl/command.rs @@ -1,11 +1,10 @@ use anyhow::{self, bail, Context}; use prettytable::Table; -use serde::Serialize; use sozu_command_lib::proto::command::{ request::RequestType, response_content::ContentType, ListWorkers, QueryCertificatesFilters, QueryClusterByDomain, QueryClustersHashes, QueryMetricsOptions, Request, Response, - ResponseContent, ResponseStatus, RunState, UpgradeMain, WorkerInfo, + ResponseContent, ResponseStatus, RunState, UpgradeMain, }; use crate::ctl::{ @@ -18,13 +17,6 @@ use crate::ctl::{ CommandManager, }; -// Used to display the JSON response of the status command -#[derive(Serialize, Debug)] -struct WorkerStatus<'a> { - pub worker: &'a WorkerInfo, - pub status: &'a String, -} - impl CommandManager { fn write_request_on_channel(&mut self, request: Request) -> anyhow::Result<()> { self.channel @@ -39,14 +31,6 @@ impl CommandManager { } pub fn send_request(&mut self, request: Request) -> Result<(), anyhow::Error> { - self.send_request_to_workers(request, false) - } - - pub fn send_request_to_workers( - &mut self, - request: Request, - json: bool, - ) -> Result<(), anyhow::Error> { self.channel .write_message(&request) .with_context(|| "Could not write the request")?; @@ -60,29 +44,22 @@ impl CommandManager { } ResponseStatus::Failure => bail!("Request failed: {}", response.message), ResponseStatus::Ok => { - if json { - // why do we need to print a success message in json? - print_json_response(&response.message)?; - } else { - println!("{}", response.message); - } + println!("{}", response.message); if let Some(response_content) = response.content { match response_content.content_type { Some(ContentType::RequestCounts(request_counts)) => { - print_request_counts(&request_counts) + print_request_counts(&request_counts, self.json)?; } Some(ContentType::FrontendList(frontends)) => { - print_frontend_list(frontends) + print_frontend_list(frontends, self.json)?; } Some(ContentType::Workers(worker_infos)) => { - if json { - print_json_response(&worker_infos)?; - } else { - print_status(worker_infos); - } + print_status(worker_infos, self.json)?; + } + Some(ContentType::ListenersList(list)) => { + print_listeners(list, self.json)?; } - Some(ContentType::ListenersList(list)) => print_listeners(list), _ => {} } } @@ -220,7 +197,6 @@ impl CommandManager { pub fn get_metrics( &mut self, - json: bool, list: bool, refresh: Option, metric_names: Vec, @@ -250,7 +226,7 @@ impl CommandManager { debug!("Proxy is processing: {}", response.message); } ResponseStatus::Failure => { - if json { + if self.json { return print_json_response(&response.message); } else { bail!("could not query proxy state: {}", response.message); @@ -260,10 +236,10 @@ impl CommandManager { if let Some(response_content) = response.content { match response_content.content_type { Some(ContentType::Metrics(aggregated_metrics_data)) => { - print_metrics(aggregated_metrics_data, json)? + print_metrics(aggregated_metrics_data, self.json)? } Some(ContentType::AvailableMetrics(available)) => { - print_available_metrics(&available)?; + print_available_metrics(&available, self.json)?; } _ => { debug!("Wrong kind of response here"); @@ -293,7 +269,6 @@ impl CommandManager { pub fn query_cluster( &mut self, - json: bool, cluster_id: Option, domain: Option, ) -> Result<(), anyhow::Error> { @@ -334,17 +309,22 @@ impl CommandManager { debug!("Proxy is processing: {}", response.message); } ResponseStatus::Failure => { - if json { + if self.json { print_json_response(&response.message)?; } bail!("could not query proxy state: {}", response.message); } ResponseStatus::Ok => { - if let Some(ResponseContent { - content_type: Some(ContentType::WorkerResponses(worker_responses)), - }) = response.content - { - print_cluster_responses(cluster_id, domain, worker_responses, json)? + match response.content { + Some(ResponseContent { + content_type: Some(ContentType::WorkerResponses(worker_responses)), + }) => print_cluster_responses( + cluster_id, + domain, + worker_responses, + self.json, + )?, + _ => bail!("Wrong response content"), } break; } @@ -356,7 +336,6 @@ impl CommandManager { pub fn query_certificates( &mut self, - json: bool, fingerprint: Option, domain: Option, query_workers: bool, @@ -367,15 +346,14 @@ impl CommandManager { }; if query_workers { - self.query_certificates_from_workers(json, filters) + self.query_certificates_from_workers(filters) } else { - self.query_certificates_from_the_state(json, filters) + self.query_certificates_from_the_state(filters) } } fn query_certificates_from_workers( &mut self, - json: bool, filters: QueryCertificatesFilters, ) -> Result<(), anyhow::Error> { self.write_request_on_channel(RequestType::QueryCertificatesFromWorkers(filters).into())?; @@ -388,19 +366,17 @@ impl CommandManager { debug!("Proxy is processing: {}", response.message); } ResponseStatus::Failure => { - if json { + if self.json { print_json_response(&response.message)?; - bail!("We received an error message"); - } else { - bail!("could not get certificate: {}", response.message); } + bail!("could not get certificate: {}", response.message); } ResponseStatus::Ok => { info!("We did get a response from the proxy"); match response.content { Some(ResponseContent { content_type: Some(ContentType::WorkerResponses(worker_responses)), - }) => print_certificates_by_worker(worker_responses.map, json)?, + }) => print_certificates_by_worker(worker_responses.map, self.json)?, _ => bail!("unexpected response: {:?}", response.content), } break; @@ -412,7 +388,6 @@ impl CommandManager { fn query_certificates_from_the_state( &mut self, - json: bool, filters: QueryCertificatesFilters, ) -> anyhow::Result<()> { self.write_request_on_channel(RequestType::QueryCertificatesFromTheState(filters).into())?; @@ -440,9 +415,8 @@ impl CommandManager { bail!("No certificates match your request."); } - if json { - print_json_response(&certs) - .with_context(|| "Could not print certificates in JSON")?; + if self.json { + print_json_response(&certs)?; } else { print_certificates_with_validity(certs) .with_context(|| "Could not show certificate")?; diff --git a/bin/src/ctl/display.rs b/bin/src/ctl/display.rs index 78a8eb66a..dea9dcad5 100644 --- a/bin/src/ctl/display.rs +++ b/bin/src/ctl/display.rs @@ -2,6 +2,8 @@ use std::collections::{BTreeMap, HashMap, HashSet}; use anyhow::{self, Context}; use prettytable::{Row, Table}; +use time::format_description; +use x509_parser::time::ASN1Time; use sozu_command_lib::proto::{ command::{ @@ -12,10 +14,11 @@ use sozu_command_lib::proto::{ }, display::concatenate_vector, }; -use time::format_description; -use x509_parser::time::ASN1Time; -pub fn print_listeners(listeners_list: ListenersList) { +pub fn print_listeners(listeners_list: ListenersList, json: bool) -> anyhow::Result<()> { + if json { + return print_json_response(&listeners_list); + } println!("\nHTTP LISTENERS\n================"); for (_, http_listener) in listeners_list.http_listeners.iter() { @@ -117,9 +120,13 @@ pub fn print_listeners(listeners_list: ListenersList) { } table.printstd(); } + Ok(()) } -pub fn print_status(worker_infos: WorkerInfos) { +pub fn print_status(worker_infos: WorkerInfos, json: bool) -> anyhow::Result<()> { + if json { + return print_json_response(&worker_infos); + } let mut table = Table::new(); table.set_format(*prettytable::format::consts::FORMAT_BOX_CHARS); table.add_row(row!["worker id", "pid", "run state"]); @@ -130,9 +137,13 @@ pub fn print_status(worker_infos: WorkerInfos) { } table.printstd(); + Ok(()) } -pub fn print_frontend_list(frontends: ListedFrontends) { +pub fn print_frontend_list(frontends: ListedFrontends, json: bool) -> anyhow::Result<()> { + if json { + return print_json_response(&frontends); + } trace!(" We received this frontends to display {:#?}", frontends); // HTTP frontends if !frontends.http_frontends.is_empty() { @@ -211,6 +222,7 @@ pub fn print_frontend_list(frontends: ListedFrontends) { } table.printstd(); } + Ok(()) } pub fn print_metrics( @@ -416,11 +428,11 @@ pub fn print_cluster_responses( worker_responses: WorkerResponses, json: bool, ) -> anyhow::Result<()> { - if let Some(needle) = cluster_id.or(domain) { - if json { - return print_json_response(&worker_responses); - } + if json { + return print_json_response(&worker_responses); + } + if let Some(needle) = cluster_id.or(domain) { let mut cluster_table = create_cluster_table( vec!["id", "sticky_session", "https_redirect"], &worker_responses.map, @@ -654,8 +666,7 @@ pub fn print_certificates_by_worker( json: bool, ) -> anyhow::Result<()> { if json { - print_json_response(&response_contents)?; - return Ok(()); + return print_json_response(&response_contents); } for (worker_id, response_content) in response_contents.iter() { @@ -690,7 +701,13 @@ fn format_tags_to_string(tags: &BTreeMap) -> String { .join(", ") } -pub fn print_available_metrics(available_metrics: &AvailableMetrics) -> anyhow::Result<()> { +pub fn print_available_metrics( + available_metrics: &AvailableMetrics, + json: bool, +) -> anyhow::Result<()> { + if json { + return print_json_response(&available_metrics); + } println!("Available metrics on the proxy level:"); for metric_name in &available_metrics.proxy_metrics { println!("\t{metric_name}"); @@ -746,7 +763,10 @@ pub fn print_certificates_with_validity( Ok(()) } -pub fn print_request_counts(request_counts: &RequestCounts) { +pub fn print_request_counts(request_counts: &RequestCounts, json: bool) -> anyhow::Result<()> { + if json { + return print_json_response(&request_counts); + } let mut table = Table::new(); table.set_format(*prettytable::format::consts::FORMAT_BOX_CHARS); table.add_row(row!["request type", "count"]); @@ -755,6 +775,7 @@ pub fn print_request_counts(request_counts: &RequestCounts) { table.add_row(row!(request_type, count)); } table.printstd(); + Ok(()) } // ISO 8601 diff --git a/bin/src/ctl/mod.rs b/bin/src/ctl/mod.rs index e80c137e9..ceeb00862 100644 --- a/bin/src/ctl/mod.rs +++ b/bin/src/ctl/mod.rs @@ -22,6 +22,8 @@ pub struct CommandManager { channel: Channel, timeout: Duration, config: Config, + /// wether to display the response in JSON + json: bool, } pub fn ctl(args: cli::Args) -> anyhow::Result<()> { @@ -49,6 +51,7 @@ pub fn ctl(args: cli::Args) -> anyhow::Result<()> { channel, timeout, config, + json: args.json, }; command_manager.handle_command(args.cmd) @@ -56,6 +59,7 @@ pub fn ctl(args: cli::Args) -> anyhow::Result<()> { impl CommandManager { fn handle_command(&mut self, command: SubCmd) -> anyhow::Result<()> { + debug!("Executing command {:?}", command); match command { SubCmd::Shutdown { hard } => { if hard { @@ -68,15 +72,15 @@ impl CommandManager { None => self.upgrade_main(), Some(worker_id) => self.upgrade_worker(worker_id), }, - SubCmd::Status { json } => self.status(json), - SubCmd::Metrics { cmd, json } => match cmd { + SubCmd::Status {} => self.status(), + SubCmd::Metrics { cmd } => match cmd { MetricsCmd::Get { list, refresh, names, clusters, backends, - } => self.get_metrics(json, list, refresh, names, clusters, backends), + } => self.get_metrics(list, refresh, names, clusters, backends), _ => self.configure_metrics(cmd), }, SubCmd::Logging { level } => self.logging_filter(&level), @@ -85,8 +89,8 @@ impl CommandManager { StateCmd::Load { file } => self.load_state(file), StateCmd::Stats => self.count_requests(), }, - SubCmd::Reload { file, json } => self.reload_configuration(file, json), - SubCmd::Cluster { cmd, json } => self.cluster_command(cmd, json), + SubCmd::Reload { file } => self.reload_configuration(file), + SubCmd::Cluster { cmd } => self.cluster_command(cmd), SubCmd::Backend { cmd } => self.backend_command(cmd), SubCmd::Frontend { cmd } => match cmd { FrontendCmd::Http { cmd } => self.http_frontend_command(cmd), @@ -105,7 +109,7 @@ impl CommandManager { ListenerCmd::Tcp { cmd } => self.tcp_listener_command(cmd), ListenerCmd::List => self.list_listeners(), }, - SubCmd::Certificate { cmd, json } => match cmd { + SubCmd::Certificate { cmd } => match cmd { CertificateCmd::Add { certificate, chain, @@ -149,7 +153,7 @@ impl CommandManager { fingerprint, domain, query_workers, - } => self.query_certificates(json, fingerprint, domain, query_workers), + } => self.query_certificates(fingerprint, domain, query_workers), }, SubCmd::Config { cmd: _ } => Ok(()), // noop, handled at the beginning of the method SubCmd::Events => self.events(), diff --git a/bin/src/ctl/request_builder.rs b/bin/src/ctl/request_builder.rs index ba9706dd0..da0151d43 100644 --- a/bin/src/ctl/request_builder.rs +++ b/bin/src/ctl/request_builder.rs @@ -42,19 +42,19 @@ impl CommandManager { pub fn soft_stop(&mut self) -> anyhow::Result<()> { debug!("shutting down proxy softly"); - self.send_request_to_workers(RequestType::SoftStop(SoftStop {}).into(), false) + self.send_request(RequestType::SoftStop(SoftStop {}).into()) } pub fn hard_stop(&mut self) -> anyhow::Result<()> { debug!("shutting down proxy the hard way"); - self.send_request_to_workers(RequestType::HardStop(HardStop {}).into(), false) + self.send_request(RequestType::HardStop(HardStop {}).into()) } - pub fn status(&mut self, json: bool) -> anyhow::Result<()> { + pub fn status(&mut self) -> anyhow::Result<()> { debug!("Requesting status…"); - self.send_request_to_workers(RequestType::Status(Status {}).into(), json) + self.send_request(RequestType::Status(Status {}).into()) } pub fn configure_metrics(&mut self, cmd: MetricsCmd) -> anyhow::Result<()> { @@ -70,13 +70,13 @@ impl CommandManager { self.send_request(RequestType::ConfigureMetrics(configuration as i32).into()) } - pub fn reload_configuration(&mut self, path: Option, json: bool) -> anyhow::Result<()> { + pub fn reload_configuration(&mut self, path: Option) -> anyhow::Result<()> { debug!("Reloading configuration…"); let path = match path { Some(p) => p, None => String::new(), }; - self.send_request_to_workers(RequestType::ReloadConfiguration(path).into(), json) + self.send_request(RequestType::ReloadConfiguration(path).into()) } pub fn list_frontends( @@ -137,7 +137,7 @@ impl CommandManager { } } - pub fn cluster_command(&mut self, cmd: ClusterCmd, json: bool) -> anyhow::Result<()> { + pub fn cluster_command(&mut self, cmd: ClusterCmd) -> anyhow::Result<()> { match cmd { ClusterCmd::Add { id, @@ -166,7 +166,7 @@ impl CommandManager { ) } ClusterCmd::Remove { id } => self.send_request(RequestType::RemoveCluster(id).into()), - ClusterCmd::List { id, domain } => self.query_cluster(json, id, domain), + ClusterCmd::List { id, domain } => self.query_cluster(id, domain), } }