From a02f01127719ea0e001c81fcace304e27c2bbafe Mon Sep 17 00:00:00 2001 From: Jonathan Grahl Date: Mon, 1 Apr 2024 15:56:42 -0500 Subject: [PATCH 1/3] feat: log tailing --- Cargo.lock | 89 +++++++++++++++++++---- Cargo.toml | 3 + src/api/mod.rs | 149 +++++++++++++++++++++++++++++---------- src/commands/services.rs | 49 +++++++++++++ src/config/user.rs | 1 + src/main.rs | 4 +- 6 files changed, 241 insertions(+), 54 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eea7238..eb1cdcf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -180,6 +180,12 @@ version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c" +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "bytes" version = "1.5.0" @@ -341,6 +347,12 @@ dependencies = [ "typenum", ] +[[package]] +name = "data-encoding" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" + [[package]] name = "dialoguer" version = "0.10.4" @@ -468,9 +480,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ "percent-encoding", ] @@ -584,7 +596,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", + "http 0.2.9", "indexmap 1.9.3", "slab", "tokio", @@ -639,6 +651,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http-body" version = "0.4.5" @@ -646,7 +669,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes", - "http", + "http 0.2.9", "pin-project-lite", ] @@ -673,7 +696,7 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http", + "http 0.2.9", "http-body", "httparse", "httpdate", @@ -693,7 +716,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" dependencies = [ "futures-util", - "http", + "http 0.2.9", "hyper", "rustls", "tokio", @@ -738,9 +761,9 @@ dependencies = [ [[package]] name = "idna" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -880,6 +903,7 @@ dependencies = [ "difference", "dirs-next", "home", + "http 1.1.0", "indexmap 2.2.5", "oauth2", "once_cell", @@ -892,6 +916,8 @@ dependencies = [ "thiserror", "tiny_http", "tracing", + "tungstenite", + "url", ] [[package]] @@ -950,7 +976,7 @@ dependencies = [ "base64 0.13.1", "chrono", "getrandom", - "http", + "http 0.2.9", "rand", "reqwest", "serde", @@ -1049,9 +1075,9 @@ checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" [[package]] name = "percent-encoding" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pest" @@ -1235,7 +1261,7 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http", + "http 0.2.9", "http-body", "hyper", "hyper-rustls", @@ -1472,6 +1498,17 @@ dependencies = [ "unsafe-libyaml", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sha2" version = "0.10.7" @@ -1760,6 +1797,24 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +[[package]] +name = "tungstenite" +version = "0.21.0" +source = "git+https://github.com/snapview/tungstenite-rs?rev=0fa4197#0fa41973b4c075f5d4a9e03a82a26a301ca31ce9" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http 1.1.0", + "httparse", + "log", + "rand", + "sha1", + "thiserror", + "url", + "utf-8", +] + [[package]] name = "typenum" version = "1.16.0" @@ -1813,9 +1868,9 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna", @@ -1823,6 +1878,12 @@ dependencies = [ "serde", ] +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "utf8parse" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index 0097911..fd4b786 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,7 @@ dialoguer = { version = "0.10.4", features = ["fuzzy-select"] } difference = "2.0" dirs-next = "2.0.0" home = "0.5.5" +http = "1.1.0" indexmap = { version = "2.2.5", features = ["serde"] } oauth2 = "4.4.2" once_cell = "1.18.0" @@ -27,3 +28,5 @@ term = "0.7.0" thiserror = "1.0.48" tiny_http = "0.12.0" tracing = "0.1.37" +tungstenite = { git = "https://github.com/snapview/tungstenite-rs", rev = "0fa4197" } +url = "2.5.0" diff --git a/src/api/mod.rs b/src/api/mod.rs index 36ca94e..bafa172 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -1,6 +1,6 @@ use anyhow::{anyhow, Context}; -use std::collections::HashMap; use reqwest::{blocking::Response, StatusCode}; +use std::collections::HashMap; use self::types::*; @@ -35,15 +35,23 @@ impl APIClient { token: &str, org_name: &str, env_name: &str, - name: &str + name: &str, ) -> anyhow::Result> { - let url = format!("{}/orgs/{}/envs/{}/svcs/{}", self.base_url, org_name, env_name, name); + let url = format!( + "{}/orgs/{}/envs/{}/svcs/{}", + self.base_url, org_name, env_name, name + ); let response = self.get(&url, token)?; match response.status() { - StatusCode::OK => Ok(serde_json::from_str(&response.text()?).with_context(|| "Failed to deserialize service")?), + StatusCode::OK => Ok(serde_json::from_str(&response.text()?) + .with_context(|| "Failed to deserialize service")?), StatusCode::UNAUTHORIZED => Err(anyhow!("Unauthorized, please login first")), StatusCode::NOT_FOUND => Ok(None), - _ => Err(anyhow!("Failed to get service. API returned {} {}", response.status(), response.text()?)) + _ => Err(anyhow!( + "Failed to get service. API returned {} {}", + response.status(), + response.text()? + )), } } @@ -51,7 +59,7 @@ impl APIClient { &self, token: &str, org_name: &str, - env_name: &str + env_name: &str, ) -> anyhow::Result { let url = format!("{}/orgs/{}/envs/{}/svcs", self.base_url, org_name, env_name); let response: String = self.get(&url, token)?.error_for_status()?.text()?; @@ -70,19 +78,24 @@ impl APIClient { body.insert("billing_email", billing_email); let response = self.post(&url, token, &body)?; match response.status() { - StatusCode::CREATED => Ok(serde_json::from_str(&response.text()?).with_context(|| "Failed to deserialize org")?), + StatusCode::CREATED => Ok(serde_json::from_str(&response.text()?) + .with_context(|| "Failed to deserialize org")?), StatusCode::UNAUTHORIZED => Err(anyhow!("Unauthorized, please login first")), StatusCode::CONFLICT => Err(anyhow!("Organization already exists")), StatusCode::NOT_FOUND => Err(anyhow!("Org not found")), StatusCode::BAD_REQUEST => Err(anyhow!("Bad request: {}", response.text()?)), - _ => Err(anyhow!("Failed to deploy service. API returned {} - {}", response.status(), response.text()?)) + _ => Err(anyhow!( + "Failed to deploy service. API returned {} - {}", + response.status(), + response.text()? + )), } } pub fn get_environments( &self, token: &str, - org_name: &str + org_name: &str, ) -> Result, reqwest::Error> { let url = format!("{}/orgs/{}/envs", self.base_url, org_name); let response = self.get(&url, token)?.error_for_status()?; @@ -93,19 +106,24 @@ impl APIClient { &self, token: &str, name: &str, - org_name: &str + org_name: &str, ) -> anyhow::Result { let url = format!("{}/orgs/{}/envs", self.base_url, org_name); let mut body = HashMap::new(); body.insert("name", name); let response = self.post(&url, token, &body)?; match response.status() { - StatusCode::CREATED => Ok(serde_json::from_str(&response.text()?).with_context(|| "Failed to deserialize env")?), + StatusCode::CREATED => Ok(serde_json::from_str(&response.text()?) + .with_context(|| "Failed to deserialize env")?), StatusCode::UNAUTHORIZED => Err(anyhow!("Unauthorized, please login first")), StatusCode::CONFLICT => Err(anyhow!("Environment already exists")), StatusCode::NOT_FOUND => Err(anyhow!("Org not found")), StatusCode::BAD_REQUEST => Err(anyhow!("Bad request: {}", response.text()?)), - _ => Err(anyhow!("Failed to create environment. API returned {} - {}", response.status(), response.text()?)) + _ => Err(anyhow!( + "Failed to create environment. API returned {} - {}", + response.status(), + response.text()? + )), } } @@ -113,14 +131,18 @@ impl APIClient { &self, token: &str, org_name: &str, - name: &str + name: &str, ) -> anyhow::Result<()> { let url = format!("{}/orgs/{}/envs/{}", self.base_url, org_name, name); let response = self.delete(&url, token)?; match response.status() { StatusCode::NO_CONTENT => Ok(()), StatusCode::NOT_FOUND => Err(anyhow!("Environment does not exist")), - _ => Err(anyhow!("Failed to delete environment. API returned {} - {}", response.status(), response.text()?)) + _ => Err(anyhow!( + "Failed to delete environment. API returned {} - {}", + response.status(), + response.text()? + )), } } @@ -129,17 +151,22 @@ impl APIClient { token: &str, org_name: &str, env_name: &str, - service: Service + service: Service, ) -> anyhow::Result { let url = format!("{}/orgs/{}/envs/{}/svcs", self.base_url, org_name, env_name); let body = serde_json::to_string(&service)?; let response = self.post_str(&url, token, body)?; match response.status() { - StatusCode::CREATED => Ok(serde_json::from_str(&response.text()?).with_context(|| "Failed to deserialize service")?), + StatusCode::CREATED => Ok(serde_json::from_str(&response.text()?) + .with_context(|| "Failed to deserialize service")?), StatusCode::UNAUTHORIZED => Err(anyhow!("Unauthorized, please login first")), StatusCode::NOT_FOUND => Err(anyhow!("Org or environment not found")), StatusCode::BAD_REQUEST => Err(anyhow!("Bad request: {}", response.text()?)), - _ => Err(anyhow!("Failed to deploy service. API returned {} - {}", response.status(), response.text()?)) + _ => Err(anyhow!( + "Failed to deploy service. API returned {} - {}", + response.status(), + response.text()? + )), } } @@ -148,14 +175,21 @@ impl APIClient { token: &str, org_name: &str, env_name: &str, - svc_name: &str + svc_name: &str, ) -> anyhow::Result<()> { - let url = format!("{}/orgs/{}/envs/{}/svcs/{}", self.base_url, org_name, env_name, svc_name); + let url = format!( + "{}/orgs/{}/envs/{}/svcs/{}", + self.base_url, org_name, env_name, svc_name + ); let response = self.delete(&url, token)?; match response.status() { StatusCode::NO_CONTENT => Ok(()), StatusCode::NOT_FOUND => Err(anyhow!("Service does not exist")), - _ => Err(anyhow!("Failed to delete service. API returned {} - {}", response.status(), response.text()?)) + _ => Err(anyhow!( + "Failed to delete service. API returned {} - {}", + response.status(), + response.text()? + )), } } @@ -163,15 +197,23 @@ impl APIClient { &self, token: &str, org_name: &str, - env_name: &str + env_name: &str, ) -> anyhow::Result { - let url = format!("{}/orgs/{}/envs/{}/secrets", self.base_url, org_name, env_name); + let url = format!( + "{}/orgs/{}/envs/{}/secrets", + self.base_url, org_name, env_name + ); let response = self.get(&url, token)?; match response.status() { - StatusCode::OK => Ok(serde_json::from_str(&response.text()?).with_context(|| "Failed to deserialize secrets list")?), + StatusCode::OK => Ok(serde_json::from_str(&response.text()?) + .with_context(|| "Failed to deserialize secrets list")?), StatusCode::UNAUTHORIZED => Err(anyhow!("Unauthorized, please login first")), StatusCode::NOT_FOUND => Err(anyhow!("Org or environment not found")), - _ => Err(anyhow!("Failed to get secrets. API returned {} - {}", response.status(), response.text()?)) + _ => Err(anyhow!( + "Failed to get secrets. API returned {} - {}", + response.status(), + response.text()? + )), } } @@ -181,9 +223,12 @@ impl APIClient { org_name: &str, env_name: &str, name: &str, - value: &str + value: &str, ) -> anyhow::Result<()> { - let url = format!("{}/orgs/{}/envs/{}/secrets/{}", self.base_url, org_name, env_name, name); + let url = format!( + "{}/orgs/{}/envs/{}/secrets/{}", + self.base_url, org_name, env_name, name + ); let mut body = HashMap::new(); body.insert("value", value); let response = self.put(&url, token, &body)?; @@ -191,7 +236,11 @@ impl APIClient { StatusCode::NO_CONTENT => Ok(()), StatusCode::UNAUTHORIZED => Err(anyhow!("Unauthorized, please login first")), StatusCode::NOT_FOUND => Err(anyhow!("Org or environment not found")), - _ => Err(anyhow!("Failed to create secret. API returned {} - {}", response.status(), response.text()?)) + _ => Err(anyhow!( + "Failed to create secret. API returned {} - {}", + response.status(), + response.text()? + )), } } @@ -200,19 +249,27 @@ impl APIClient { token: &str, org_name: &str, env_name: &str, - secret_name: &str + secret_name: &str, ) -> anyhow::Result<()> { - let url = format!("{}/orgs/{}/envs/{}/secrets/{}", self.base_url, org_name, env_name, secret_name); + let url = format!( + "{}/orgs/{}/envs/{}/secrets/{}", + self.base_url, org_name, env_name, secret_name + ); let response = self.delete(&url, token)?; match response.status() { StatusCode::NO_CONTENT => Ok(()), StatusCode::NOT_FOUND => Err(anyhow!("Secret does not exist")), - _ => Err(anyhow!("Failed to delete secret. API returned {} - {}", response.status(), response.text()?)) + _ => Err(anyhow!( + "Failed to delete secret. API returned {} - {}", + response.status(), + response.text()? + )), } } fn get(&self, url: &str, token: &str) -> Result { - return self.client + return self + .client .get(url) .header("User-Agent", self.user_agent.as_str()) .header("Authorization", format!("Bearer {}", token)) @@ -220,28 +277,41 @@ impl APIClient { .send(); } - fn put(&self, url: &str, token: &str, body: &HashMap<&str, &str>) -> Result { - return self.client + fn put( + &self, + url: &str, + token: &str, + body: &HashMap<&str, &str>, + ) -> Result { + return self + .client .put(url) .header("User-Agent", self.user_agent.as_str()) .header("Authorization", format!("Bearer {}", token)) .header("Content-Type", "application/json") .json(&body) - .send() + .send(); } - fn post(&self, url: &str, token: &str, body: &HashMap<&str, &str>) -> Result { - return self.client + fn post( + &self, + url: &str, + token: &str, + body: &HashMap<&str, &str>, + ) -> Result { + return self + .client .post(url) .header("User-Agent", self.user_agent.as_str()) .header("Authorization", format!("Bearer {}", token)) .header("Content-Type", "application/json") .json(&body) - .send() + .send(); } fn post_str(&self, url: &str, token: &str, body: String) -> Result { - return self.client + return self + .client .post(url) .header("User-Agent", self.user_agent.as_str()) .header("Authorization", format!("Bearer {}", token)) @@ -251,7 +321,8 @@ impl APIClient { } fn delete(&self, url: &str, token: &str) -> Result { - return self.client + return self + .client .delete(url) .header("User-Agent", self.user_agent.as_str()) .header("Authorization", format!("Bearer {}", token)) diff --git a/src/commands/services.rs b/src/commands/services.rs index a957714..850a68f 100644 --- a/src/commands/services.rs +++ b/src/commands/services.rs @@ -3,12 +3,15 @@ use anyhow::{anyhow, Result}; use clap::{Parser, Subcommand}; use dialoguer::{FuzzySelect, Input}; use difference::{Changeset, Difference}; +use http::Uri; use serde::Deserialize; use std::fs::File; use std::io::Read; use std::io::Write; use std::path::Path; use tabled::Table; +use tungstenite::connect; +use tungstenite::ClientRequestBuilder; use crate::{ api::types::Service, @@ -382,3 +385,49 @@ impl Delete { Ok(()) } } + +#[derive(Debug, Parser)] +pub struct Logs { + #[arg(help = "Path to molnett manifest", default_value("./molnett.yaml"))] + manifest: String, +} + +impl Logs { + pub fn execute(&self, base: &CommandBase) -> Result<()> { + let org_name = base.get_org()?; + let token = base + .user_config() + .get_token() + .ok_or_else(|| anyhow!("No token found. Please login first."))?; + + let manifest = self.read_manifest()?; + + let logurl: Uri = format!( + "{}/orgs/{}/envs/{}/svcs/{}/logs", + base.user_config().get_url().replace("http", "ws"), + org_name, + manifest.environment, + manifest.service.name, + ) + .parse() + .expect("Could not parse url"); + + let builder = ClientRequestBuilder::new(logurl) + .with_header("Authorization", format!("Bearer {}", token.to_owned())); + + let (mut socket, _) = connect(builder).expect("Could not connect"); + + loop { + let msg = socket.read().expect("Error reading message"); + println!("{}", msg.to_string().trim_end()); + } + } + + fn read_manifest(&self) -> Result { + let file_path = self.manifest.clone(); + let mut file_content = String::new(); + File::open(file_path)?.read_to_string(&mut file_content)?; + let manifest = serde_yaml::from_str(&file_content)?; + Ok(manifest) + } +} diff --git a/src/config/user.rs b/src/config/user.rs index 2f4ae47..569804d 100644 --- a/src/config/user.rs +++ b/src/config/user.rs @@ -1,5 +1,6 @@ use camino::Utf8PathBuf; use config::Config; +use tungstenite::http::Uri; use crate::Cli; diff --git a/src/main.rs b/src/main.rs index 508608e..391b100 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,6 @@ mod api; mod commands; mod config; - #[derive(Debug, Parser)] #[command( author, @@ -56,6 +55,8 @@ enum Commands { Environments(commands::environments::Environments), /// Deploy a service Deploy(commands::services::Deploy), + /// Tail logs from a service + Logs(commands::services::Logs), /// Generate Dockerfile and Molnett manifest Initialize(commands::services::Initialize), /// Manage organizations @@ -80,6 +81,7 @@ fn main() -> Result<()> { Some(Commands::Auth(auth)) => auth.execute(&mut base), Some(Commands::Environments(environments)) => environments.execute(&mut base), Some(Commands::Deploy(deploy)) => deploy.execute(&mut base), + Some(Commands::Logs(logs)) => logs.execute(&mut base), Some(Commands::Initialize(init)) => init.execute(&mut base), Some(Commands::Orgs(orgs)) => orgs.execute(&mut base), Some(Commands::Secrets(secrets)) => secrets.execute(&mut base), From bee90f72b56e56f736323585cae84881aa48e671 Mon Sep 17 00:00:00 2001 From: Jonathan Grahl Date: Mon, 1 Apr 2024 16:01:04 -0500 Subject: [PATCH 2/3] fix: unnecessary import --- Cargo.lock | 1 - Cargo.toml | 1 - src/commands/services.rs | 3 +-- src/config/user.rs | 2 +- 4 files changed, 2 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index eb1cdcf..5a3f969 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -903,7 +903,6 @@ dependencies = [ "difference", "dirs-next", "home", - "http 1.1.0", "indexmap 2.2.5", "oauth2", "once_cell", diff --git a/Cargo.toml b/Cargo.toml index fd4b786..9d9351d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,6 @@ dialoguer = { version = "0.10.4", features = ["fuzzy-select"] } difference = "2.0" dirs-next = "2.0.0" home = "0.5.5" -http = "1.1.0" indexmap = { version = "2.2.5", features = ["serde"] } oauth2 = "4.4.2" once_cell = "1.18.0" diff --git a/src/commands/services.rs b/src/commands/services.rs index 850a68f..89a6ca9 100644 --- a/src/commands/services.rs +++ b/src/commands/services.rs @@ -3,7 +3,6 @@ use anyhow::{anyhow, Result}; use clap::{Parser, Subcommand}; use dialoguer::{FuzzySelect, Input}; use difference::{Changeset, Difference}; -use http::Uri; use serde::Deserialize; use std::fs::File; use std::io::Read; @@ -402,7 +401,7 @@ impl Logs { let manifest = self.read_manifest()?; - let logurl: Uri = format!( + let logurl: tungstenite::http::Uri = format!( "{}/orgs/{}/envs/{}/svcs/{}/logs", base.user_config().get_url().replace("http", "ws"), org_name, diff --git a/src/config/user.rs b/src/config/user.rs index 569804d..28f4120 100644 --- a/src/config/user.rs +++ b/src/config/user.rs @@ -1,6 +1,6 @@ use camino::Utf8PathBuf; use config::Config; -use tungstenite::http::Uri; + use crate::Cli; From 64800cda3bf8c59a1bf689401dc94462b7e01036 Mon Sep 17 00:00:00 2001 From: Jonathan Grahl Date: Mon, 1 Apr 2024 16:50:27 -0500 Subject: [PATCH 3/3] fix: construct url correctly --- Cargo.lock | 1 + Cargo.toml | 4 +++- src/commands/services.rs | 21 +++++++++++++-------- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5a3f969..74173ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1807,6 +1807,7 @@ dependencies = [ "http 1.1.0", "httparse", "log", + "native-tls", "rand", "sha1", "thiserror", diff --git a/Cargo.toml b/Cargo.toml index 9d9351d..026bc2b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,5 +27,7 @@ term = "0.7.0" thiserror = "1.0.48" tiny_http = "0.12.0" tracing = "0.1.37" -tungstenite = { git = "https://github.com/snapview/tungstenite-rs", rev = "0fa4197" } +tungstenite = { git = "https://github.com/snapview/tungstenite-rs", rev = "0fa4197", features = [ + "native-tls", +] } url = "2.5.0" diff --git a/src/commands/services.rs b/src/commands/services.rs index 89a6ca9..faa81ab 100644 --- a/src/commands/services.rs +++ b/src/commands/services.rs @@ -10,6 +10,7 @@ use std::io::Write; use std::path::Path; use tabled::Table; use tungstenite::connect; +use tungstenite::http::Uri; use tungstenite::ClientRequestBuilder; use crate::{ @@ -400,16 +401,20 @@ impl Logs { .ok_or_else(|| anyhow!("No token found. Please login first."))?; let manifest = self.read_manifest()?; - - let logurl: tungstenite::http::Uri = format!( - "{}/orgs/{}/envs/{}/svcs/{}/logs", - base.user_config().get_url().replace("http", "ws"), - org_name, - manifest.environment, - manifest.service.name, + let logurl: Uri = url::Url::parse( + format!( + "{}/orgs/{}/envs/{}/svcs/{}/logs", + base.user_config().get_url().replace("http", "ws"), + org_name, + manifest.environment, + manifest.service.name, + ) + .as_str(), ) + .unwrap() + .as_str() .parse() - .expect("Could not parse url"); + .unwrap(); let builder = ClientRequestBuilder::new(logurl) .with_header("Authorization", format!("Bearer {}", token.to_owned()));