From cede4221abc590a8085b6bf57b26a918a58edb16 Mon Sep 17 00:00:00 2001 From: Joshua Jerin Date: Wed, 10 Jan 2024 17:58:33 -0500 Subject: [PATCH 01/12] adding logs.rs --- tembo-cli/src/cmd/logs.rs | 55 +++++++++++++++++++++++++++++++++++++++ tembo-cli/src/cmd/mod.rs | 1 + tembo-cli/src/main.rs | 7 ++++- 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 tembo-cli/src/cmd/logs.rs diff --git a/tembo-cli/src/cmd/logs.rs b/tembo-cli/src/cmd/logs.rs new file mode 100644 index 000000000..947b4ea71 --- /dev/null +++ b/tembo-cli/src/cmd/logs.rs @@ -0,0 +1,55 @@ +use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use anyhow::Result; +use clap::Args; + +#[derive(Args)] +pub struct LogsCommand { + #[clap(short, long)] + pub verbose: bool, +} + + +impl LogsCommand { + pub async fn execute(&self) -> Result<()> { + let org_id = "****"; + let instance_id = "****"; + let token = "****"; // Replace with your actual JWT token + + let client = reqwest::Client::new(); + let mut headers = HeaderMap::new(); + headers.insert("X-Scope-OrgID", HeaderValue::from_str(org_id)?); + headers.insert(AUTHORIZATION, HeaderValue::from_str(&format!("Bearer {}", token))?); + + let query = format!("{{tembo_instance_id=\"{}\"}}", instance_id); + let url = "https://api.data-1.use1.tembo.io/loki/api/v1/query_range"; + + let response = client + .get(url) + .headers(headers) + .query(&[("query", &query)]) + .send() + .await?; + + if response.status().is_success() { + let response_body = response.text().await?; + + println!("{}",response_body); + + /*for stream in parsed_response.data.result { + println!("Stream: {:?}", stream.stream); + for value in stream.values { + println!("Time: {}", value[0]); + println!("Log: {}", value[1]); + println!("-----------------------"); + } + }*/ + } else { + eprintln!("Error: {:?}", response.status()); + } + + Ok(()) + } +} + diff --git a/tembo-cli/src/cmd/mod.rs b/tembo-cli/src/cmd/mod.rs index 37f51abff..a4185887a 100644 --- a/tembo-cli/src/cmd/mod.rs +++ b/tembo-cli/src/cmd/mod.rs @@ -3,3 +3,4 @@ pub mod context; pub mod delete; pub mod init; pub mod validate; +pub mod logs; diff --git a/tembo-cli/src/main.rs b/tembo-cli/src/main.rs index fd7665e61..5a162db19 100644 --- a/tembo-cli/src/main.rs +++ b/tembo-cli/src/main.rs @@ -1,8 +1,9 @@ use crate::cmd::delete::DeleteCommand; use crate::cmd::validate::ValidateCommand; -use crate::cmd::{apply, context, delete, init, validate}; +use crate::cmd::{apply, context, delete, init, validate, logs}; use clap::{crate_authors, crate_version, Args, Parser, Subcommand}; use cmd::apply::ApplyCommand; +use cmd::logs::LogsCommand; use cmd::context::{ContextCommand, ContextSubCommand}; use cmd::init::InitCommand; @@ -28,6 +29,7 @@ enum SubCommands { Apply(ApplyCommand), Validate(ValidateCommand), Delete(DeleteCommand), + Logs(LogsCommand) } #[derive(Args)] @@ -58,6 +60,9 @@ fn main() -> Result<(), anyhow::Error> { SubCommands::Validate(_validate_cmd) => { validate::execute(app.global_opts.verbose)?; } + SubCommands::Logs(_logs_cmd) => { + tokio::runtime::Runtime::new().unwrap().block_on(_logs_cmd.execute())?; + } SubCommands::Delete(_delete_cmd) => { delete::execute()?; } From aa105172f9f66982264a17cfaa3aa186bb2b91dd Mon Sep 17 00:00:00 2001 From: Joshua Jerin Date: Tue, 16 Jan 2024 10:27:59 -0500 Subject: [PATCH 02/12] fetching org_id and instance_id --- tembo-cli/src/cmd/apply.rs | 6 +--- tembo-cli/src/cmd/logs.rs | 70 ++++++++++++++++++++++---------------- tembo-cli/src/main.rs | 2 +- 3 files changed, 42 insertions(+), 36 deletions(-) diff --git a/tembo-cli/src/cmd/apply.rs b/tembo-cli/src/cmd/apply.rs index af7e73b74..6c381e08e 100644 --- a/tembo-cli/src/cmd/apply.rs +++ b/tembo-cli/src/cmd/apply.rs @@ -55,11 +55,7 @@ pub fn execute(verbose: bool, _merge_path: Option) -> Result<(), anyhow: let env = get_current_context()?; - if env.target == Target::Docker.to_string() { - return execute_docker(verbose, _merge_path); - } else if env.target == Target::TemboCloud.to_string() { - return execute_tembo_cloud(env.clone(), _merge_path); - } + return execute_tembo_cloud(env.clone(), _merge_path); Ok(()) } diff --git a/tembo-cli/src/cmd/logs.rs b/tembo-cli/src/cmd/logs.rs index 947b4ea71..c7a5b5415 100644 --- a/tembo-cli/src/cmd/logs.rs +++ b/tembo-cli/src/cmd/logs.rs @@ -1,8 +1,14 @@ +use crate::apply::{get_instance_id, get_instance_settings}; // Adjust the path as needed +use crate::cli::context::{get_current_context, Environment, Profile}; +use reqwest::blocking::Client; use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION}; -use serde::{Deserialize, Serialize}; -use std::collections::HashMap; use anyhow::Result; use clap::Args; +use temboclient::{ + apis::{ + configuration::Configuration, + }, +}; #[derive(Args)] pub struct LogsCommand { @@ -10,46 +16,50 @@ pub struct LogsCommand { pub verbose: bool, } +pub fn execute(verbose: bool) -> Result<()> { + let env = get_current_context()?; + let org_id = env.org_id.clone().unwrap_or_default(); + let profile = env.selected_profile.clone().unwrap(); -impl LogsCommand { - pub async fn execute(&self) -> Result<()> { - let org_id = "****"; - let instance_id = "****"; - let token = "****"; // Replace with your actual JWT token + let config = Configuration { + base_path: profile.tembo_host, + bearer_access_token: Some(profile.tembo_access_token.clone()), + ..Default::default() + }; - let client = reqwest::Client::new(); - let mut headers = HeaderMap::new(); - headers.insert("X-Scope-OrgID", HeaderValue::from_str(org_id)?); - headers.insert(AUTHORIZATION, HeaderValue::from_str(&format!("Bearer {}", token))?); + let instance_settings = get_instance_settings(None)?; + + let client = Client::new(); + let mut headers = HeaderMap::new(); + headers.insert("X-Scope-OrgID", HeaderValue::from_str(&org_id)?); + headers.insert(AUTHORIZATION, HeaderValue::from_str(&format!("Bearer {}", profile.tembo_access_token))?); + + for (_key, value) in instance_settings.iter() { + let instance_id_option = get_instance_id(value.instance_name.clone(), &config, env.clone())?; + + let instance_id = if let Some(id) = instance_id_option { + id + } else { + eprintln!("Instance ID not found for {}", value.instance_name); + continue; + }; let query = format!("{{tembo_instance_id=\"{}\"}}", instance_id); let url = "https://api.data-1.use1.tembo.io/loki/api/v1/query_range"; let response = client .get(url) - .headers(headers) + .headers(headers.clone()) .query(&[("query", &query)]) - .send() - .await?; - + .send()?; + if response.status().is_success() { - let response_body = response.text().await?; - - println!("{}",response_body); - - /*for stream in parsed_response.data.result { - println!("Stream: {:?}", stream.stream); - for value in stream.values { - println!("Time: {}", value[0]); - println!("Log: {}", value[1]); - println!("-----------------------"); - } - }*/ + let response_body = response.text()?; + println!("{}", response_body); } else { eprintln!("Error: {:?}", response.status()); } - - Ok(()) } -} + Ok(()) +} diff --git a/tembo-cli/src/main.rs b/tembo-cli/src/main.rs index 5a162db19..e2ed6d2fd 100644 --- a/tembo-cli/src/main.rs +++ b/tembo-cli/src/main.rs @@ -61,7 +61,7 @@ fn main() -> Result<(), anyhow::Error> { validate::execute(app.global_opts.verbose)?; } SubCommands::Logs(_logs_cmd) => { - tokio::runtime::Runtime::new().unwrap().block_on(_logs_cmd.execute())?; + logs::execute(app.global_opts.verbose); } SubCommands::Delete(_delete_cmd) => { delete::execute()?; From b87710c4eab1ead0b1c444be70bc1689cb14dabc Mon Sep 17 00:00:00 2001 From: Joshua Jerin Date: Tue, 16 Jan 2024 19:47:21 -0500 Subject: [PATCH 03/12] adding fmt changes --- tembo-cli/src/cmd/logs.rs | 20 ++++++++++---------- tembo-cli/src/cmd/mod.rs | 2 +- tembo-cli/src/main.rs | 6 +++--- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tembo-cli/src/cmd/logs.rs b/tembo-cli/src/cmd/logs.rs index c7a5b5415..e3e2b6f8c 100644 --- a/tembo-cli/src/cmd/logs.rs +++ b/tembo-cli/src/cmd/logs.rs @@ -1,14 +1,10 @@ use crate::apply::{get_instance_id, get_instance_settings}; // Adjust the path as needed use crate::cli::context::{get_current_context, Environment, Profile}; -use reqwest::blocking::Client; -use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION}; use anyhow::Result; use clap::Args; -use temboclient::{ - apis::{ - configuration::Configuration, - }, -}; +use reqwest::blocking::Client; +use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION}; +use temboclient::apis::configuration::Configuration; #[derive(Args)] pub struct LogsCommand { @@ -32,10 +28,14 @@ pub fn execute(verbose: bool) -> Result<()> { let client = Client::new(); let mut headers = HeaderMap::new(); headers.insert("X-Scope-OrgID", HeaderValue::from_str(&org_id)?); - headers.insert(AUTHORIZATION, HeaderValue::from_str(&format!("Bearer {}", profile.tembo_access_token))?); + headers.insert( + AUTHORIZATION, + HeaderValue::from_str(&format!("Bearer {}", profile.tembo_access_token))?, + ); for (_key, value) in instance_settings.iter() { - let instance_id_option = get_instance_id(value.instance_name.clone(), &config, env.clone())?; + let instance_id_option = + get_instance_id(value.instance_name.clone(), &config, env.clone())?; let instance_id = if let Some(id) = instance_id_option { id @@ -52,7 +52,7 @@ pub fn execute(verbose: bool) -> Result<()> { .headers(headers.clone()) .query(&[("query", &query)]) .send()?; - + if response.status().is_success() { let response_body = response.text()?; println!("{}", response_body); diff --git a/tembo-cli/src/cmd/mod.rs b/tembo-cli/src/cmd/mod.rs index a4185887a..ff5811910 100644 --- a/tembo-cli/src/cmd/mod.rs +++ b/tembo-cli/src/cmd/mod.rs @@ -2,5 +2,5 @@ pub mod apply; pub mod context; pub mod delete; pub mod init; -pub mod validate; pub mod logs; +pub mod validate; diff --git a/tembo-cli/src/main.rs b/tembo-cli/src/main.rs index e2ed6d2fd..e0655e56a 100644 --- a/tembo-cli/src/main.rs +++ b/tembo-cli/src/main.rs @@ -1,11 +1,11 @@ use crate::cmd::delete::DeleteCommand; use crate::cmd::validate::ValidateCommand; -use crate::cmd::{apply, context, delete, init, validate, logs}; +use crate::cmd::{apply, context, delete, init, logs, validate}; use clap::{crate_authors, crate_version, Args, Parser, Subcommand}; use cmd::apply::ApplyCommand; -use cmd::logs::LogsCommand; use cmd::context::{ContextCommand, ContextSubCommand}; use cmd::init::InitCommand; +use cmd::logs::LogsCommand; mod cli; mod cmd; @@ -29,7 +29,7 @@ enum SubCommands { Apply(ApplyCommand), Validate(ValidateCommand), Delete(DeleteCommand), - Logs(LogsCommand) + Logs(LogsCommand), } #[derive(Args)] From 85c4b45d65b165817692eea2c1292a01db7116c1 Mon Sep 17 00:00:00 2001 From: Joshua Jerin Date: Tue, 16 Jan 2024 21:23:19 -0500 Subject: [PATCH 04/12] minor changes --- tembo-cli/src/cmd/apply.rs | 6 +++++- tembo-cli/src/cmd/logs.rs | 2 +- tembo-cli/tests/tomls/merge/tembo.toml | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/tembo-cli/src/cmd/apply.rs b/tembo-cli/src/cmd/apply.rs index 97b32cafb..7a55ecd33 100644 --- a/tembo-cli/src/cmd/apply.rs +++ b/tembo-cli/src/cmd/apply.rs @@ -55,7 +55,11 @@ pub fn execute(verbose: bool, _merge_path: Option) -> Result<(), anyhow: let env = get_current_context()?; - return execute_tembo_cloud(env.clone(), _merge_path); + if env.target == Target::Docker.to_string() { + return execute_docker(verbose, _merge_path); + } else if env.target == Target::TemboCloud.to_string() { + return execute_tembo_cloud(env.clone(), _merge_path); + } Ok(()) } diff --git a/tembo-cli/src/cmd/logs.rs b/tembo-cli/src/cmd/logs.rs index e3e2b6f8c..ae1f87709 100644 --- a/tembo-cli/src/cmd/logs.rs +++ b/tembo-cli/src/cmd/logs.rs @@ -1,4 +1,4 @@ -use crate::apply::{get_instance_id, get_instance_settings}; // Adjust the path as needed +use crate::apply::{get_instance_id, get_instance_settings}; use crate::cli::context::{get_current_context, Environment, Profile}; use anyhow::Result; use clap::Args; diff --git a/tembo-cli/tests/tomls/merge/tembo.toml b/tembo-cli/tests/tomls/merge/tembo.toml index d3ada1af5..79a9477af 100644 --- a/tembo-cli/tests/tomls/merge/tembo.toml +++ b/tembo-cli/tests/tomls/merge/tembo.toml @@ -1,7 +1,7 @@ [defaults] environment = "dev" -instance_name = "defaults_instance" +instance_name = "defaults" cpu = "1" -storage = "50Gi" +storage = "10Gi" replicas = 1 stack_type = "Standard" \ No newline at end of file From 83e8b6e1b70b89a0822fa987fdb1366303d5a193 Mon Sep 17 00:00:00 2001 From: Joshua Jerin Date: Mon, 22 Jan 2024 14:16:34 -0500 Subject: [PATCH 05/12] removing unnecessary changes --- tembo-cli/tests/tomls/merge/tembo.toml | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 tembo-cli/tests/tomls/merge/tembo.toml diff --git a/tembo-cli/tests/tomls/merge/tembo.toml b/tembo-cli/tests/tomls/merge/tembo.toml deleted file mode 100644 index b18d3cb91..000000000 --- a/tembo-cli/tests/tomls/merge/tembo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[defaults] -environment = "dev" -instance_name = "defaults" -cpu = "1" -storage = "10Gi" -replicas = 1 -stack_type = "Standard" From 71764d5ff1b72cef89e38f2c22dbd9741a007efe Mon Sep 17 00:00:00 2001 From: Joshua Jerin Date: Mon, 22 Jan 2024 14:49:56 -0500 Subject: [PATCH 06/12] adding simple parse to logs --- tembo-cli/src/cmd/logs.rs | 58 ++++++++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 7 deletions(-) diff --git a/tembo-cli/src/cmd/logs.rs b/tembo-cli/src/cmd/logs.rs index ae1f87709..06c94ab28 100644 --- a/tembo-cli/src/cmd/logs.rs +++ b/tembo-cli/src/cmd/logs.rs @@ -2,6 +2,7 @@ use crate::apply::{get_instance_id, get_instance_settings}; use crate::cli::context::{get_current_context, Environment, Profile}; use anyhow::Result; use clap::Args; +use serde::{Deserialize, Serialize}; use reqwest::blocking::Client; use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION}; use temboclient::apis::configuration::Configuration; @@ -12,6 +13,49 @@ pub struct LogsCommand { pub verbose: bool, } +#[derive(Serialize, Deserialize, Debug)] +struct LogStream { + app: String, + container: String, + pod: String, + stream: String, + tembo_instance_id: String, + tembo_organization_id: String, +} + +#[derive(Serialize, Deserialize, Debug)] +struct LogEntry { + stream: LogStream, + values: Vec>, +} + +#[derive(Serialize, Deserialize, Debug)] +struct LogResult { + resultType: String, + result: Vec, +} + +#[derive(Serialize, Deserialize, Debug)] +struct LogData { + status: String, + data: LogResult, +} + +fn beautify_logs(json_data: &str) -> Result<()> { + let log_data: LogData = serde_json::from_str(json_data)?; + + for entry in log_data.data.result { + println!("\nApp: {}, Container: {}, Pod: {}", entry.stream.app, entry.stream.container, entry.stream.pod); + for value in entry.values { + let log_message = &value[1]; + println!("{}", log_message); + println!("--------------------------------"); + } + } + + Ok(()) +} + pub fn execute(verbose: bool) -> Result<()> { let env = get_current_context()?; let org_id = env.org_id.clone().unwrap_or_default(); @@ -23,7 +67,7 @@ pub fn execute(verbose: bool) -> Result<()> { ..Default::default() }; - let instance_settings = get_instance_settings(None)?; + let instance_settings = get_instance_settings(None,None)?; let client = Client::new(); let mut headers = HeaderMap::new(); @@ -53,12 +97,12 @@ pub fn execute(verbose: bool) -> Result<()> { .query(&[("query", &query)]) .send()?; - if response.status().is_success() { - let response_body = response.text()?; - println!("{}", response_body); - } else { - eprintln!("Error: {:?}", response.status()); - } + if response.status().is_success() { + let response_body = response.text()?; + beautify_logs(&response_body)?; + } else { + eprintln!("Error: {:?}", response.status()); + } } Ok(()) From d915a2404844cf22797551b3af82e1030aa86cfc Mon Sep 17 00:00:00 2001 From: Joshua Jerin Date: Mon, 22 Jan 2024 14:57:00 -0500 Subject: [PATCH 07/12] minor preetify --- tembo-cli/src/cmd/logs.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tembo-cli/src/cmd/logs.rs b/tembo-cli/src/cmd/logs.rs index 06c94ab28..25f0c1567 100644 --- a/tembo-cli/src/cmd/logs.rs +++ b/tembo-cli/src/cmd/logs.rs @@ -45,11 +45,10 @@ fn beautify_logs(json_data: &str) -> Result<()> { let log_data: LogData = serde_json::from_str(json_data)?; for entry in log_data.data.result { - println!("\nApp: {}, Container: {}, Pod: {}", entry.stream.app, entry.stream.container, entry.stream.pod); + //println!("\nApp: {}, Container: {}, Pod: {}", entry.stream.app, entry.stream.container, entry.stream.pod); for value in entry.values { let log_message = &value[1]; - println!("{}", log_message); - println!("--------------------------------"); + println!("\n{}", log_message); } } From 18b90616f52e64093dfcf1e12413f8d7a4565ba3 Mon Sep 17 00:00:00 2001 From: Joshua Jerin Date: Mon, 22 Jan 2024 14:58:21 -0500 Subject: [PATCH 08/12] minor fmt change --- tembo-cli/src/cmd/logs.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tembo-cli/src/cmd/logs.rs b/tembo-cli/src/cmd/logs.rs index 25f0c1567..ca70e3468 100644 --- a/tembo-cli/src/cmd/logs.rs +++ b/tembo-cli/src/cmd/logs.rs @@ -2,9 +2,9 @@ use crate::apply::{get_instance_id, get_instance_settings}; use crate::cli::context::{get_current_context, Environment, Profile}; use anyhow::Result; use clap::Args; -use serde::{Deserialize, Serialize}; use reqwest::blocking::Client; use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION}; +use serde::{Deserialize, Serialize}; use temboclient::apis::configuration::Configuration; #[derive(Args)] @@ -66,7 +66,7 @@ pub fn execute(verbose: bool) -> Result<()> { ..Default::default() }; - let instance_settings = get_instance_settings(None,None)?; + let instance_settings = get_instance_settings(None, None)?; let client = Client::new(); let mut headers = HeaderMap::new(); @@ -96,12 +96,12 @@ pub fn execute(verbose: bool) -> Result<()> { .query(&[("query", &query)]) .send()?; - if response.status().is_success() { - let response_body = response.text()?; - beautify_logs(&response_body)?; - } else { - eprintln!("Error: {:?}", response.status()); - } + if response.status().is_success() { + let response_body = response.text()?; + beautify_logs(&response_body)?; + } else { + eprintln!("Error: {:?}", response.status()); + } } Ok(()) From 72b9cc8f659159dba9a2cc1ba99cef5ff4d45b72 Mon Sep 17 00:00:00 2001 From: Joshua Jerin Date: Wed, 24 Jan 2024 17:37:50 -0500 Subject: [PATCH 09/12] format logs and removing verbose --- tembo-cli/src/cmd/logs.rs | 21 +++++++++++++++------ tembo-cli/src/main.rs | 2 +- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/tembo-cli/src/cmd/logs.rs b/tembo-cli/src/cmd/logs.rs index ca70e3468..90b5b330d 100644 --- a/tembo-cli/src/cmd/logs.rs +++ b/tembo-cli/src/cmd/logs.rs @@ -9,8 +9,6 @@ use temboclient::apis::configuration::Configuration; #[derive(Args)] pub struct LogsCommand { - #[clap(short, long)] - pub verbose: bool, } #[derive(Serialize, Deserialize, Debug)] @@ -41,21 +39,32 @@ struct LogData { data: LogResult, } +#[derive(Serialize, Deserialize, Debug)] +struct IndividualLogEntry { + ts: String, + msg: String, +} + fn beautify_logs(json_data: &str) -> Result<()> { let log_data: LogData = serde_json::from_str(json_data)?; for entry in log_data.data.result { - //println!("\nApp: {}, Container: {}, Pod: {}", entry.stream.app, entry.stream.container, entry.stream.pod); for value in entry.values { - let log_message = &value[1]; - println!("\n{}", log_message); + let log_json = &value[1]; // Assuming this is where the log JSON is stored + let log_entry: IndividualLogEntry = serde_json::from_str(log_json)?; + + println!("{}", format_log_entry(&log_entry)); } } Ok(()) } -pub fn execute(verbose: bool) -> Result<()> { +fn format_log_entry(log_entry: &IndividualLogEntry) -> String { + format!("{} {}", log_entry.ts, log_entry.msg) +} + +pub fn execute() -> Result<()> { let env = get_current_context()?; let org_id = env.org_id.clone().unwrap_or_default(); let profile = env.selected_profile.clone().unwrap(); diff --git a/tembo-cli/src/main.rs b/tembo-cli/src/main.rs index bd5e2417a..f2ef5cd84 100644 --- a/tembo-cli/src/main.rs +++ b/tembo-cli/src/main.rs @@ -65,7 +65,7 @@ fn main() -> Result<(), anyhow::Error> { validate::execute(app.global_opts.verbose)?; } SubCommands::Logs(_logs_cmd) => { - logs::execute(app.global_opts.verbose); + logs::execute()?; } SubCommands::Delete(_delete_cmd) => { delete::execute()?; From 7f3bf37aba45ba29e65c4153377fb91e39013206 Mon Sep 17 00:00:00 2001 From: Joshua Jerin Date: Thu, 25 Jan 2024 10:34:27 -0500 Subject: [PATCH 10/12] read from credentials --- tembo-cli/src/cmd/logs.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tembo-cli/src/cmd/logs.rs b/tembo-cli/src/cmd/logs.rs index 90b5b330d..c2bd0f62c 100644 --- a/tembo-cli/src/cmd/logs.rs +++ b/tembo-cli/src/cmd/logs.rs @@ -8,8 +8,7 @@ use serde::{Deserialize, Serialize}; use temboclient::apis::configuration::Configuration; #[derive(Args)] -pub struct LogsCommand { -} +pub struct LogsCommand {} #[derive(Serialize, Deserialize, Debug)] struct LogStream { @@ -68,6 +67,7 @@ pub fn execute() -> Result<()> { let env = get_current_context()?; let org_id = env.org_id.clone().unwrap_or_default(); let profile = env.selected_profile.clone().unwrap(); + let tembo_data_host = profile.clone().tembo_data_host; let config = Configuration { base_path: profile.tembo_host, @@ -97,7 +97,7 @@ pub fn execute() -> Result<()> { }; let query = format!("{{tembo_instance_id=\"{}\"}}", instance_id); - let url = "https://api.data-1.use1.tembo.io/loki/api/v1/query_range"; + let url = format!("{}/loki/api/v1/query_range", tembo_data_host); let response = client .get(url) From 328f2588d2a6b0340f9ed20a8e3913eb1b421838 Mon Sep 17 00:00:00 2001 From: Joshua Jerin Date: Thu, 25 Jan 2024 12:05:01 -0500 Subject: [PATCH 11/12] adding unit tests --- tembo-cli/src/cmd/logs.rs | 194 ++++++++++++++++++++++++-------------- 1 file changed, 124 insertions(+), 70 deletions(-) diff --git a/tembo-cli/src/cmd/logs.rs b/tembo-cli/src/cmd/logs.rs index c2bd0f62c..eadf0c181 100644 --- a/tembo-cli/src/cmd/logs.rs +++ b/tembo-cli/src/cmd/logs.rs @@ -1,6 +1,6 @@ use crate::apply::{get_instance_id, get_instance_settings}; use crate::cli::context::{get_current_context, Environment, Profile}; -use anyhow::Result; +use anyhow::{Result, anyhow}; use clap::Args; use reqwest::blocking::Client; use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION}; @@ -12,106 +12,160 @@ pub struct LogsCommand {} #[derive(Serialize, Deserialize, Debug)] struct LogStream { - app: String, - container: String, - pod: String, - stream: String, - tembo_instance_id: String, - tembo_organization_id: String, +app: String, +container: String, +pod: String, +stream: String, +tembo_instance_id: String, +tembo_organization_id: String, } #[derive(Serialize, Deserialize, Debug)] struct LogEntry { - stream: LogStream, - values: Vec>, +stream: LogStream, +values: Vec>, } #[derive(Serialize, Deserialize, Debug)] struct LogResult { - resultType: String, - result: Vec, +resultType: String, +result: Vec, } #[derive(Serialize, Deserialize, Debug)] struct LogData { - status: String, - data: LogResult, +status: String, +data: LogResult, } #[derive(Serialize, Deserialize, Debug)] struct IndividualLogEntry { - ts: String, - msg: String, +ts: String, +msg: String, } fn beautify_logs(json_data: &str) -> Result<()> { - let log_data: LogData = serde_json::from_str(json_data)?; - - for entry in log_data.data.result { - for value in entry.values { - let log_json = &value[1]; // Assuming this is where the log JSON is stored - let log_entry: IndividualLogEntry = serde_json::from_str(log_json)?; - - println!("{}", format_log_entry(&log_entry)); +let log_data: LogData = serde_json::from_str(json_data)?; + +for entry in log_data.data.result { + for value in entry.values { + let log = &value[1]; + + match serde_json::from_str::(log) { + Ok(log_entry) => println!("{}", format_log_entry(&log_entry)), + Err(_) => println!("{}", log), } } +} - Ok(()) +Ok(()) } fn format_log_entry(log_entry: &IndividualLogEntry) -> String { - format!("{} {}", log_entry.ts, log_entry.msg) +format!("{} {}", log_entry.ts, log_entry.msg) } pub fn execute() -> Result<()> { - let env = get_current_context()?; - let org_id = env.org_id.clone().unwrap_or_default(); - let profile = env.selected_profile.clone().unwrap(); - let tembo_data_host = profile.clone().tembo_data_host; - - let config = Configuration { - base_path: profile.tembo_host, - bearer_access_token: Some(profile.tembo_access_token.clone()), - ..Default::default() +let env = get_current_context()?; +let org_id = env.org_id.clone().unwrap_or_default(); +let profile = env.selected_profile.clone().unwrap(); +let tembo_data_host = profile.clone().tembo_data_host; + +let config = Configuration { + base_path: profile.tembo_host, + bearer_access_token: Some(profile.tembo_access_token.clone()), + ..Default::default() +}; + +let instance_settings = get_instance_settings(None, None)?; + +let client = Client::new(); +let mut headers = HeaderMap::new(); +headers.insert("X-Scope-OrgID", HeaderValue::from_str(&org_id)?); +headers.insert( + AUTHORIZATION, + HeaderValue::from_str(&format!("Bearer {}", profile.tembo_access_token))?, +); + +for (_key, value) in instance_settings.iter() { + let instance_id_option = + get_instance_id(value.instance_name.clone(), &config, env.clone())?; + + let instance_id = if let Some(id) = instance_id_option { + id + } else { + eprintln!("Instance ID not found for {}", value.instance_name); + continue; }; - let instance_settings = get_instance_settings(None, None)?; - - let client = Client::new(); - let mut headers = HeaderMap::new(); - headers.insert("X-Scope-OrgID", HeaderValue::from_str(&org_id)?); - headers.insert( - AUTHORIZATION, - HeaderValue::from_str(&format!("Bearer {}", profile.tembo_access_token))?, - ); - - for (_key, value) in instance_settings.iter() { - let instance_id_option = - get_instance_id(value.instance_name.clone(), &config, env.clone())?; - - let instance_id = if let Some(id) = instance_id_option { - id - } else { - eprintln!("Instance ID not found for {}", value.instance_name); - continue; - }; - - let query = format!("{{tembo_instance_id=\"{}\"}}", instance_id); - let url = format!("{}/loki/api/v1/query_range", tembo_data_host); - - let response = client - .get(url) - .headers(headers.clone()) - .query(&[("query", &query)]) - .send()?; - - if response.status().is_success() { - let response_body = response.text()?; - beautify_logs(&response_body)?; - } else { - eprintln!("Error: {:?}", response.status()); + let query = format!("{{tembo_instance_id=\"{}\"}}", instance_id); + let url = format!("{}/loki/api/v1/query_range", tembo_data_host); + + let response = client + .get(url) + .headers(headers.clone()) + .query(&[("query", &query)]) + .send()?; + + if response.status().is_success() { + let response_body = response.text()?; + beautify_logs(&response_body)?; + } else { + eprintln!("Error: {:?}", response.status()); + } +} + +Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn mock_query(query: &str) -> Result { + match query { + "valid_json" => Ok(r#"{ + "status": "success", + "data": { + "resultType": "matrix", + "result": [ + { + "stream": { + "app": "test_app", + "container": "test_container", + "pod": "test_pod", + "stream": "test_stream", + "tembo_instance_id": "test_id", + "tembo_organization_id": "test_org_id" + }, + "values": [ + ["1234567890", "{\"ts\":\"2024-01-24T21:37:53Z\",\"msg\":\"Valid JSON log entry\"}"] + ] + }, + { + "stream": { + "app": "test_app", + "container": "test_container", + "pod": "test_pod", + "stream": "test_stream", + "tembo_instance_id": "test_id", + "tembo_organization_id": "test_org_id" + }, + "values": [ + ["1234567890", "Non-JSON log entry"] + ] + } + ] + } + }"#.to_string()), + _ => Err(anyhow!("Invalid query")), } } - Ok(()) + #[tokio::test] + async fn cloud_logs() { + let valid_json_log = mock_query("valid_json").unwrap(); + let result = beautify_logs(&valid_json_log); + assert!(result.is_ok()); + } } From 27188294d74c9798b570776576b68f2429f8dc18 Mon Sep 17 00:00:00 2001 From: Joshua Jerin Date: Thu, 25 Jan 2024 12:06:10 -0500 Subject: [PATCH 12/12] fmt changes --- tembo-cli/src/cmd/logs.rs | 148 +++++++++++++++++++------------------- 1 file changed, 74 insertions(+), 74 deletions(-) diff --git a/tembo-cli/src/cmd/logs.rs b/tembo-cli/src/cmd/logs.rs index eadf0c181..3588b0b4e 100644 --- a/tembo-cli/src/cmd/logs.rs +++ b/tembo-cli/src/cmd/logs.rs @@ -1,6 +1,6 @@ use crate::apply::{get_instance_id, get_instance_settings}; use crate::cli::context::{get_current_context, Environment, Profile}; -use anyhow::{Result, anyhow}; +use anyhow::{anyhow, Result}; use clap::Args; use reqwest::blocking::Client; use reqwest::header::{HeaderMap, HeaderValue, AUTHORIZATION}; @@ -12,116 +12,116 @@ pub struct LogsCommand {} #[derive(Serialize, Deserialize, Debug)] struct LogStream { -app: String, -container: String, -pod: String, -stream: String, -tembo_instance_id: String, -tembo_organization_id: String, + app: String, + container: String, + pod: String, + stream: String, + tembo_instance_id: String, + tembo_organization_id: String, } #[derive(Serialize, Deserialize, Debug)] struct LogEntry { -stream: LogStream, -values: Vec>, + stream: LogStream, + values: Vec>, } #[derive(Serialize, Deserialize, Debug)] struct LogResult { -resultType: String, -result: Vec, + resultType: String, + result: Vec, } #[derive(Serialize, Deserialize, Debug)] struct LogData { -status: String, -data: LogResult, + status: String, + data: LogResult, } #[derive(Serialize, Deserialize, Debug)] struct IndividualLogEntry { -ts: String, -msg: String, + ts: String, + msg: String, } fn beautify_logs(json_data: &str) -> Result<()> { -let log_data: LogData = serde_json::from_str(json_data)?; - -for entry in log_data.data.result { - for value in entry.values { - let log = &value[1]; - - match serde_json::from_str::(log) { - Ok(log_entry) => println!("{}", format_log_entry(&log_entry)), - Err(_) => println!("{}", log), + let log_data: LogData = serde_json::from_str(json_data)?; + + for entry in log_data.data.result { + for value in entry.values { + let log = &value[1]; + + match serde_json::from_str::(log) { + Ok(log_entry) => println!("{}", format_log_entry(&log_entry)), + Err(_) => println!("{}", log), + } } } -} -Ok(()) + Ok(()) } fn format_log_entry(log_entry: &IndividualLogEntry) -> String { -format!("{} {}", log_entry.ts, log_entry.msg) + format!("{} {}", log_entry.ts, log_entry.msg) } pub fn execute() -> Result<()> { -let env = get_current_context()?; -let org_id = env.org_id.clone().unwrap_or_default(); -let profile = env.selected_profile.clone().unwrap(); -let tembo_data_host = profile.clone().tembo_data_host; - -let config = Configuration { - base_path: profile.tembo_host, - bearer_access_token: Some(profile.tembo_access_token.clone()), - ..Default::default() -}; - -let instance_settings = get_instance_settings(None, None)?; - -let client = Client::new(); -let mut headers = HeaderMap::new(); -headers.insert("X-Scope-OrgID", HeaderValue::from_str(&org_id)?); -headers.insert( - AUTHORIZATION, - HeaderValue::from_str(&format!("Bearer {}", profile.tembo_access_token))?, -); - -for (_key, value) in instance_settings.iter() { - let instance_id_option = - get_instance_id(value.instance_name.clone(), &config, env.clone())?; - - let instance_id = if let Some(id) = instance_id_option { - id - } else { - eprintln!("Instance ID not found for {}", value.instance_name); - continue; + let env = get_current_context()?; + let org_id = env.org_id.clone().unwrap_or_default(); + let profile = env.selected_profile.clone().unwrap(); + let tembo_data_host = profile.clone().tembo_data_host; + + let config = Configuration { + base_path: profile.tembo_host, + bearer_access_token: Some(profile.tembo_access_token.clone()), + ..Default::default() }; - let query = format!("{{tembo_instance_id=\"{}\"}}", instance_id); - let url = format!("{}/loki/api/v1/query_range", tembo_data_host); - - let response = client - .get(url) - .headers(headers.clone()) - .query(&[("query", &query)]) - .send()?; - - if response.status().is_success() { - let response_body = response.text()?; - beautify_logs(&response_body)?; - } else { - eprintln!("Error: {:?}", response.status()); + let instance_settings = get_instance_settings(None, None)?; + + let client = Client::new(); + let mut headers = HeaderMap::new(); + headers.insert("X-Scope-OrgID", HeaderValue::from_str(&org_id)?); + headers.insert( + AUTHORIZATION, + HeaderValue::from_str(&format!("Bearer {}", profile.tembo_access_token))?, + ); + + for (_key, value) in instance_settings.iter() { + let instance_id_option = + get_instance_id(value.instance_name.clone(), &config, env.clone())?; + + let instance_id = if let Some(id) = instance_id_option { + id + } else { + eprintln!("Instance ID not found for {}", value.instance_name); + continue; + }; + + let query = format!("{{tembo_instance_id=\"{}\"}}", instance_id); + let url = format!("{}/loki/api/v1/query_range", tembo_data_host); + + let response = client + .get(url) + .headers(headers.clone()) + .query(&[("query", &query)]) + .send()?; + + if response.status().is_success() { + let response_body = response.text()?; + beautify_logs(&response_body)?; + } else { + eprintln!("Error: {:?}", response.status()); + } } -} -Ok(()) + Ok(()) } #[cfg(test)] mod tests { use super::*; - + fn mock_query(query: &str) -> Result { match query { "valid_json" => Ok(r#"{