From 6f621bd40ee669ddf37290d8d10306d72ef52688 Mon Sep 17 00:00:00 2001 From: Oliver Stenbom Date: Thu, 17 Aug 2023 16:24:00 +0200 Subject: [PATCH] Add more useful print statements to most commands (#16) --- linkup-cli/src/background_booting.rs | 26 ++++++++--- linkup-cli/src/background_tunnel.rs | 7 ++- linkup-cli/src/local_config.rs | 6 +-- linkup-cli/src/local_server.rs | 25 +++++------ linkup-cli/src/main.rs | 4 +- linkup-cli/src/remote_local.rs | 46 ++++++++++++++++---- linkup-cli/src/start.rs | 5 ++- linkup-cli/src/status.rs | 65 +++++++++++++++++----------- 8 files changed, 121 insertions(+), 63 deletions(-) diff --git a/linkup-cli/src/background_booting.rs b/linkup-cli/src/background_booting.rs index 5debe82..e56b0f9 100644 --- a/linkup-cli/src/background_booting.rs +++ b/linkup-cli/src/background_booting.rs @@ -16,6 +16,7 @@ use crate::background_local_server::{ use crate::background_tunnel::start_tunnel; use crate::local_config::{LocalState, ServiceTarget}; use crate::start::save_state; +use crate::status::print_session_names; use crate::{start::get_state, CliError}; use crate::{LINKUP_ENV_SEPARATOR, LINKUP_LOCALSERVER_PORT}; @@ -26,16 +27,20 @@ pub fn boot_background_services() -> Result<(), CliError> { .expect("linkup url invalid"); if is_local_server_started().is_err() { - println!("starting linkup local server..."); + println!("Starting linkup local server..."); start_local_server()?; + } else { + println!("Linkup local server was already running.. Try stopping linkup first if you have problems."); } wait_till_ok(format!("{}linkup-check", local_url))?; if is_tunnel_started().is_err() { - println!("starting tunnel..."); + println!("Starting tunnel..."); let tunnel = start_tunnel()?; state.linkup.tunnel = tunnel; + } else { + println!("Cloudflare tunnel was already running.. Try stopping linkup first if you have problems."); } for service in &state.services { @@ -61,21 +66,28 @@ pub fn boot_background_services() -> Result<(), CliError> { let tunnel_url = state.linkup.tunnel.clone(); state.linkup.session_name = server_session_name.clone(); + let state_to_print = state.clone(); + save_state(state)?; - println!("{}", server_session_name); + println!("Waiting for tunnel to be ready at {}...", tunnel_url); // If the tunnel is checked too quickly, it dies ¯\_(ツ)_/¯ thread::sleep(Duration::from_millis(1000)); wait_till_ok(format!("{}linkup-check", tunnel_url))?; - // final checks services are responding - // print status + println!(); + + print_session_names(&state_to_print); Ok(()) } -fn load_config(url: &Url, desired_name: &str, config: StorableSession) -> Result { +pub fn load_config( + url: &Url, + desired_name: &str, + config: StorableSession, +) -> Result { let client = Client::new(); let endpoint = url .join("/linkup") @@ -112,7 +124,7 @@ fn load_config(url: &Url, desired_name: &str, config: StorableSession) -> Result } } -fn server_config_from_state(state: &LocalState) -> (StorableSession, StorableSession) { +pub fn server_config_from_state(state: &LocalState) -> (StorableSession, StorableSession) { let local_server_services = state .services .iter() diff --git a/linkup-cli/src/background_tunnel.rs b/linkup-cli/src/background_tunnel.rs index 10fcbde..fe148cc 100644 --- a/linkup-cli/src/background_tunnel.rs +++ b/linkup-cli/src/background_tunnel.rs @@ -26,7 +26,12 @@ pub fn start_tunnel() -> Result { attempt += 1; match try_start_tunnel() { Ok(url) => return Ok(url), - Err(CliError::StopErr(e)) => return Err(CliError::StopErr(format!("Failed to stop tunnel when retrying tunnel boot: {}", e))), + Err(CliError::StopErr(e)) => { + return Err(CliError::StopErr(format!( + "Failed to stop tunnel when retrying tunnel boot: {}", + e + ))) + } Err(err) => { println!("Tunnel failed to boot within the time limit. Retrying..."); if attempt >= 3 { diff --git a/linkup-cli/src/local_config.rs b/linkup-cli/src/local_config.rs index 6f46054..25e7eda 100644 --- a/linkup-cli/src/local_config.rs +++ b/linkup-cli/src/local_config.rs @@ -6,14 +6,14 @@ use url::Url; use linkup::{StorableDomain, StorableRewrite}; -#[derive(Deserialize, Serialize)] +#[derive(Deserialize, Serialize, Clone)] pub struct LocalState { pub linkup: LinkupState, pub domains: Vec, pub services: Vec, } -#[derive(Deserialize, Serialize)] +#[derive(Deserialize, Serialize, Clone)] pub struct LinkupState { pub session_name: String, pub session_token: String, @@ -82,7 +82,7 @@ pub fn config_to_state(yaml_config: YamlLocalConfig, config_path: String) -> Loc session_token: random_token, config_path, remote: yaml_config.linkup.remote, - tunnel: Url::parse("http://localhost").expect("default url parses"), + tunnel: Url::parse("http://tunnel-not-yet-set").expect("default url parses"), cache_routes: yaml_config.linkup.cache_routes, }; diff --git a/linkup-cli/src/local_server.rs b/linkup-cli/src/local_server.rs index 1248c41..c152dba 100644 --- a/linkup-cli/src/local_server.rs +++ b/linkup-cli/src/local_server.rs @@ -1,9 +1,9 @@ use std::{collections::HashMap, io}; -use futures::stream::StreamExt; use actix_web::{ - http::header, middleware, web, App, HttpRequest, HttpResponse, HttpServer, Responder, guard, rt, + guard, http::header, middleware, rt, web, App, HttpRequest, HttpResponse, HttpServer, Responder, }; +use futures::stream::StreamExt; use thiserror::Error; use linkup::*; @@ -55,7 +55,7 @@ async fn linkup_config_handler( async fn linkup_ws_request_handler( string_store: web::Data, req: HttpRequest, - req_stream: web::Payload + req_stream: web::Payload, ) -> impl Responder { let sessions = SessionAllocator::new(string_store.into_inner()); @@ -117,10 +117,8 @@ async fn linkup_ws_request_handler( let status = response.status().as_u16(); if status != 101 { return HttpResponse::BadGateway() - .append_header(header::ContentType::plaintext()) - .body( - "The underlying server did not accept the websocket connection.", - ); + .append_header(header::ContentType::plaintext()) + .body("The underlying server did not accept the websocket connection."); } // Copy headers from the target back to the client. @@ -136,11 +134,11 @@ async fn linkup_ws_request_handler( let upgrade_result = response.upgrade().await; let upgrade = match upgrade_result { Ok(response) => response, - Err(_) => return HttpResponse::BadGateway() - .append_header(header::ContentType::plaintext()) - .body( - "could not upgrade to websocket connection.", - ), + Err(_) => { + return HttpResponse::BadGateway() + .append_header(header::ContentType::plaintext()) + .body("could not upgrade to websocket connection.") + } }; let (target_rx, mut target_tx) = tokio::io::split(upgrade); @@ -176,7 +174,6 @@ async fn linkup_request_handler( .map(|(k, v)| (k.to_string(), v.to_str().unwrap_or("").to_string())) .collect::>(); - let session_result = sessions .get_request_session(url.clone(), headers.clone()) .await; @@ -296,7 +293,7 @@ pub async fn local_linkup_main() -> io::Result<()> { .service( web::resource("{path:.*}") .guard(guard::Header("upgrade", "websocket")) - .to(linkup_ws_request_handler) + .to(linkup_ws_request_handler), ) .default_service(web::route().to(linkup_request_handler)) }) diff --git a/linkup-cli/src/main.rs b/linkup-cli/src/main.rs index c666404..01f27ac 100644 --- a/linkup-cli/src/main.rs +++ b/linkup-cli/src/main.rs @@ -114,9 +114,9 @@ enum Commands { }, #[clap(about = "Stop a running linkup session")] Stop {}, - #[clap(about = "Configure your linkup session to route traffic to a local service")] + #[clap(about = "Route session traffic to a local service")] Local { service_name: String }, - #[clap(about = "Configure your linkup session to route traffic to a remote service")] + #[clap(about = "Route session traffic to a remote service")] Remote { service_name: String }, #[clap(about = "View linkup component and service status")] Status { diff --git a/linkup-cli/src/remote_local.rs b/linkup-cli/src/remote_local.rs index 2be69d4..d1c2e8b 100644 --- a/linkup-cli/src/remote_local.rs +++ b/linkup-cli/src/remote_local.rs @@ -1,14 +1,16 @@ +use url::Url; + use crate::{ - background_booting::boot_background_services, - local_config::ServiceTarget, + background_booting::{load_config, server_config_from_state}, + local_config::{LocalState, ServiceTarget}, start::{get_state, save_state}, - CliError, + CliError, LINKUP_LOCALSERVER_PORT, }; pub fn remote(service_name: String) -> Result<(), CliError> { let mut state = get_state()?; - let mut service = state + let service = state .services .iter_mut() .find(|s| s.name == service_name) @@ -18,8 +20,13 @@ pub fn remote(service_name: String) -> Result<(), CliError> { )))?; service.current = ServiceTarget::Remote; - save_state(state)?; - boot_background_services()?; + save_state(state.clone())?; + load_server_states(state)?; + + println!( + "Linkup is routing {} traffic to the remote server", + service_name + ); Ok(()) } @@ -27,7 +34,7 @@ pub fn remote(service_name: String) -> Result<(), CliError> { pub fn local(service_name: String) -> Result<(), CliError> { let mut state = get_state()?; - let mut service = state + let service = state .services .iter_mut() .find(|s| s.name == service_name) @@ -37,7 +44,28 @@ pub fn local(service_name: String) -> Result<(), CliError> { )))?; service.current = ServiceTarget::Local; - save_state(state)?; - boot_background_services()?; + save_state(state.clone())?; + load_server_states(state)?; + + println!( + "Linkup is routing {} traffic to the local server", + service_name + ); + + Ok(()) +} + +fn load_server_states(state: LocalState) -> Result<(), CliError> { + let local_url = Url::parse(&format!("http://localhost:{}", LINKUP_LOCALSERVER_PORT)) + .expect("linkup url invalid"); + + let (local_server_conf, remote_server_conf) = server_config_from_state(&state); + let _ = load_config( + &state.linkup.remote, + &state.linkup.session_name.clone(), + remote_server_conf, + )?; + let _ = load_config(&local_url, &state.linkup.session_name, local_server_conf)?; + Ok(()) } diff --git a/linkup-cli/src/start.rs b/linkup-cli/src/start.rs index ee4f249..6efae4d 100644 --- a/linkup-cli/src/start.rs +++ b/linkup-cli/src/start.rs @@ -11,8 +11,6 @@ use crate::{ }; pub fn start(config_arg: Option) -> Result<(), CliError> { - // TODO: run `stop` to kill the previous local server? - let previous_state = get_state(); let config_path = config_path(config_arg)?; let input_config = get_config(config_path.clone())?; @@ -23,6 +21,9 @@ pub fn start(config_arg: Option) -> Result<(), CliError> { if let Ok(ps) = previous_state { state.linkup.session_name = ps.linkup.session_name; state.linkup.session_token = ps.linkup.session_token; + + // Maintain tunnel state until it is rewritten + state.linkup.tunnel = ps.linkup.tunnel; } save_state(state)?; diff --git a/linkup-cli/src/status.rs b/linkup-cli/src/status.rs index bc3e46b..e2d7768 100644 --- a/linkup-cli/src/status.rs +++ b/linkup-cli/src/status.rs @@ -71,26 +71,10 @@ pub fn status(json: bool) -> Result<(), CliError> { .then(a.name.cmp(&b.name)) }); - // Filter out domains that are subdomains of other domains - let filtered_domains = state - .domains - .iter() - .filter(|&d| { - !state - .domains - .iter() - .any(|other| other.domain != d.domain && d.domain.ends_with(&other.domain)) - }) - .map(|d| d.domain.clone()) - .collect::>(); - let status = Status { session: SessionStatus { name: state.linkup.session_name.clone(), - domains: filtered_domains - .iter() - .map(|d| format!("{}.{}", state.linkup.session_name.clone(), d.clone())) - .collect(), + domains: format_state_domains(&state), }, services, }; @@ -101,14 +85,7 @@ pub fn status(json: bool) -> Result<(), CliError> { serde_json::to_string_pretty(&status).expect("Failed to serialize status") ); } else { - // Display session information - println!("Session Information:"); - println!(" Session Name: {}", status.session.name); - println!(" Domains: "); - for domain in &status.session.domains { - println!(" {}", domain); - } - println!(); + print_session_status(status.session); // Display services information println!("Service Information:"); @@ -140,6 +117,44 @@ pub fn status(json: bool) -> Result<(), CliError> { Ok(()) } +pub fn print_session_names(state: &LocalState) { + print_session_status(SessionStatus { + name: state.linkup.session_name.clone(), + domains: format_state_domains(state), + }); +} + +fn format_state_domains(state: &LocalState) -> Vec { + // Filter out domains that are subdomains of other domains + let filtered_domains = state + .domains + .iter() + .filter(|&d| { + !state + .domains + .iter() + .any(|other| other.domain != d.domain && d.domain.ends_with(&other.domain)) + }) + .map(|d| d.domain.clone()) + .collect::>(); + + return filtered_domains + .iter() + .map(|d| format!("{}.{}", state.linkup.session_name.clone(), d.clone())) + .collect(); +} + +fn print_session_status(session: SessionStatus) { + // Display session information + println!("Session Information:"); + println!(" Session Name: {}", session.name); + println!(" Domains: "); + for domain in session.domains { + println!(" {}", domain); + } + println!(); +} + fn linkup_status(tx: std::sync::mpsc::Sender, state: &LocalState) { let local_url = format!("http://localhost:{}", LINKUP_LOCALSERVER_PORT);