Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: shuffle code and responsibility of LocalState #49

Merged
merged 8 commits into from
Nov 20, 2023
117 changes: 66 additions & 51 deletions linkup-cli/src/background_booting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,13 @@ 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::worker_client::WorkerClient;
use crate::CliError;
use crate::LINKUP_LOCALSERVER_PORT;
use crate::{start::get_state, CliError};

pub fn boot_background_services() -> Result<(), CliError> {
let mut state = get_state()?;
let mut state = LocalState::load()?;

let local_url = Url::parse(&format!("http://localhost:{}", LINKUP_LOCALSERVER_PORT))
.expect("linkup url invalid");
Expand All @@ -40,35 +39,34 @@ pub fn boot_background_services() -> Result<(), CliError> {
println!("Cloudflare tunnel was already running.. Try stopping linkup first if you have problems.");
}

let (local_server_conf, remote_server_conf) = server_config_from_state(&state);
let server_config = ServerConfig::from(&state);

let server_session_name = load_config(
&state.linkup.remote,
&state.linkup.session_name,
remote_server_conf,
server_config.remote,
)?;
let local_session_name = load_config(&local_url, &server_session_name, local_server_conf)?;
let local_session_name = load_config(&local_url, &server_session_name, server_config.local)?;
Comment on lines +42 to +49
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok 👍 Cool!


if server_session_name != local_session_name {
return Err(CliError::InconsistentState);
}

let tunnel_url = state.linkup.tunnel.clone();
state.linkup.session_name = server_session_name;
state.save()?;

state.linkup.session_name = server_session_name.clone();
let state_to_print = state.clone();

save_state(state)?;

println!("Waiting for tunnel to be ready at {}...", tunnel_url);
println!(
"Waiting for tunnel to be ready at {}...",
&state.linkup.tunnel
);

// If the tunnel is checked too quickly, it dies ¯\_(ツ)_/¯
thread::sleep(Duration::from_millis(1000));
wait_till_ok(format!("{}linkup-check", tunnel_url))?;
wait_till_ok(format!("{}linkup-check", &state.linkup.tunnel))?;

println!();

print_session_names(&state_to_print);
print_session_names(&state);

Ok(())
}
Expand All @@ -80,7 +78,7 @@ pub fn load_config(
) -> Result<String, CliError> {
let session_update_req = UpdateSessionRequest {
session_token: config.session_token,
desired_name: desired_name.into(),
desired_name: desired_name.to_string(),
services: config.services,
domains: config.domains,
cache_routes: config.cache_routes,
Expand All @@ -93,49 +91,66 @@ pub fn load_config(
Ok(content)
}

pub fn server_config_from_state(state: &LocalState) -> (StorableSession, StorableSession) {
let local_server_services = state
.services
.iter()
.map(|local_service| StorableService {
name: local_service.name.clone(),
location: if local_service.current == ServiceTarget::Remote {
local_service.remote.clone()
} else {
local_service.local.clone()
},
rewrites: Some(local_service.rewrites.clone()),
})
.collect::<Vec<StorableService>>();

let remote_server_services = state
.services
.iter()
.map(|local_service| StorableService {
name: local_service.name.clone(),
location: if local_service.current == ServiceTarget::Remote {
local_service.remote.clone()
} else {
state.linkup.tunnel.clone()
},
rewrites: Some(local_service.rewrites.clone()),
})
.collect::<Vec<StorableService>>();

(
StorableSession {
pub struct ServerConfig {
pub local: StorableSession,
pub remote: StorableSession,
}

impl From<&LocalState> for ServerConfig {
fn from(state: &LocalState) -> Self {
let local_server_services = state
.services
.iter()
.map(|service| StorableService {
name: service.name.clone(),
location: if service.current == ServiceTarget::Remote {
service.remote.clone()
} else {
service.local.clone()
},
rewrites: Some(service.rewrites.clone()),
})
.collect::<Vec<StorableService>>();

let remote_server_services = state
.services
.iter()
.map(|service| StorableService {
name: service.name.clone(),
location: if service.current == ServiceTarget::Remote {
service.remote.clone()
} else {
state.linkup.tunnel.clone()
},
rewrites: Some(service.rewrites.clone()),
})
.collect::<Vec<StorableService>>();

let local_storable_session = StorableSession {
session_token: state.linkup.session_token.clone(),
services: local_server_services,
domains: state.domains.clone(),
cache_routes: state.linkup.cache_routes.clone(),
},
StorableSession {
};

let remote_storable_session = StorableSession {
session_token: state.linkup.session_token.clone(),
services: remote_server_services,
domains: state.domains.clone(),
cache_routes: state.linkup.cache_routes.clone(),
},
)
};

ServerConfig {
local: local_storable_session,
remote: remote_storable_session,
}
}
}

impl<'a> From<&'a ServerConfig> for (&'a StorableSession, &'a StorableSession) {
fn from(config: &'a ServerConfig) -> Self {
(&config.local, &config.remote)
}
}

pub fn wait_till_ok(url: String) -> Result<(), CliError> {
Expand Down
40 changes: 39 additions & 1 deletion linkup-cli/src/local_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use url::Url;

use linkup::{CreatePreviewRequest, StorableDomain, StorableRewrite, StorableService};

use crate::{CliError, LINKUP_CONFIG_ENV};
use crate::{linkup_file_path, CliError, LINKUP_CONFIG_ENV, LINKUP_STATE_FILE};

#[derive(Deserialize, Serialize, Clone)]
pub struct LocalState {
Expand All @@ -19,6 +19,44 @@ pub struct LocalState {
pub services: Vec<LocalService>,
}

impl LocalState {
pub fn load() -> Result<Self, CliError> {
if let Err(e) = fs::File::open(linkup_file_path(LINKUP_STATE_FILE)) {
return Err(CliError::NoState(e.to_string()));
}

let content = match fs::read_to_string(linkup_file_path(LINKUP_STATE_FILE)) {
Ok(content) => content,
Err(e) => return Err(CliError::NoState(e.to_string())),
};

match serde_yaml::from_str(&content) {
Ok(config) => Ok(config),
Err(e) => Err(CliError::NoState(e.to_string())),
}
}

pub fn save(&mut self) -> Result<(), CliError> {
let yaml_string = match serde_yaml::to_string(self) {
Ok(yaml) => yaml,
Err(_) => {
return Err(CliError::SaveState(
"Failed to serialize the state into YAML".to_string(),
))
}
};

if fs::write(linkup_file_path(LINKUP_STATE_FILE), yaml_string).is_err() {
return Err(CliError::SaveState(format!(
"Failed to write the state file at {}",
linkup_file_path(LINKUP_STATE_FILE).display()
)));
}

Ok(())
}
}

#[derive(Deserialize, Serialize, Clone)]
pub struct LinkupState {
pub session_name: String,
Expand Down
18 changes: 9 additions & 9 deletions linkup-cli/src/remote_local.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
use url::Url;

use crate::{
background_booting::{load_config, server_config_from_state},
background_booting::{load_config, ServerConfig},
local_config::{LocalState, ServiceTarget},
start::{get_state, save_state},
CliError, LINKUP_LOCALSERVER_PORT,
};

Expand All @@ -13,7 +12,7 @@ pub fn remote(service_names: &[String]) -> Result<(), CliError> {
"No service names provided".to_string(),
));
}
let mut state = get_state()?;
let mut state = LocalState::load()?;

for service_name in service_names {
let service = state
Expand All @@ -24,7 +23,7 @@ pub fn remote(service_names: &[String]) -> Result<(), CliError> {
service.current = ServiceTarget::Remote;
}

save_state(state.clone())?;
state.save()?;
load_server_states(state)?;

println!(
Expand All @@ -42,7 +41,7 @@ pub fn local(service_names: &[String]) -> Result<(), CliError> {
));
}

let mut state = get_state()?;
let mut state = LocalState::load()?;

for service_name in service_names {
let service = state
Expand All @@ -53,7 +52,7 @@ pub fn local(service_names: &[String]) -> Result<(), CliError> {
service.current = ServiceTarget::Local;
}

save_state(state.clone())?;
state.save()?;
load_server_states(state)?;

println!(
Expand All @@ -68,13 +67,14 @@ 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 server_config = ServerConfig::from(&state);

let _ = load_config(
&state.linkup.remote,
&state.linkup.session_name.clone(),
remote_server_conf,
server_config.remote,
)?;
let _ = load_config(&local_url, &state.linkup.session_name, local_server_conf)?;
let _ = load_config(&local_url, &state.linkup.session_name, server_config.local)?;

Ok(())
}
6 changes: 3 additions & 3 deletions linkup-cli/src/reset.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
use crate::{
background_booting::boot_background_services,
linkup_file_path,
local_config::{config_path, get_config},
start::{boot_local_dns, get_state},
local_config::{config_path, get_config, LocalState},
start::boot_local_dns,
stop::shutdown,
CliError, LINKUP_LOCALDNS_INSTALL,
};

// TODO(ostenbom)[2023-09-26]: Config arg shouldn't be needed here, we could use config state for this
pub fn reset(config_arg: &Option<String>) -> Result<(), CliError> {
// Ensure there is some kind of state from before, otherwise reset doesn't make sense
get_state()?;
LocalState::load()?;

shutdown()?;
boot_background_services()?;
Expand Down
Loading