diff options
Diffstat (limited to 'src/cli')
| -rw-r--r-- | src/cli/machine.rs | 34 | ||||
| -rw-r--r-- | src/cli/status.rs | 2 | ||||
| -rw-r--r-- | src/cli/target.rs | 46 |
3 files changed, 75 insertions, 7 deletions
diff --git a/src/cli/machine.rs b/src/cli/machine.rs index ab84041e..53404c03 100644 --- a/src/cli/machine.rs +++ b/src/cli/machine.rs @@ -164,18 +164,21 @@ fn add(args: &[String]) -> std::io::Result<i32> { return Ok(2); } } - if let Err(error) = crate::remote::prepare_saved_ssh(&target, &session) { - eprintln!("error: {error}; machine was not saved"); - crate::remote::print_saved_ssh_error_hint(&error, &target); - return Ok(1); - } + let metadata = match crate::remote::prepare_saved_ssh(&target, &session) { + Ok(metadata) => metadata, + Err(error) => { + eprintln!("error: {error}; machine was not saved"); + crate::remote::print_saved_ssh_error_hint(&error, &target); + return Ok(1); + } + }; // Setup can wait for human approval. Do not overwrite catalog edits made meanwhile. let mut catalog = load_catalog().map_err(|error| { std::io::Error::other(format!( "remote prepared, but machine was not saved: {error}" )) })?; - let id = match catalog.add_ssh(label, target, session) { + let id = match catalog.add_ssh(label, &target, &session) { Ok(id) => id, Err(error) => { eprintln!("error: {error}"); @@ -187,6 +190,10 @@ fn add(args: &[String]) -> std::io::Result<i32> { "remote prepared, but machine was not saved: {error}" )) })?; + if let Some(metadata) = metadata { + crate::client::endpoint::SshMetadataCache::new(id.as_str(), &target, &session)? + .store(&metadata); + } println!("Saved SSH machine {id}. Remote server is ready."); println!("Open Herdr clients connect automatically."); Ok(0) @@ -232,11 +239,26 @@ fn remove(args: &[String]) -> std::io::Result<i32> { }; let mut catalog = load_catalog()?; let previous_selection = catalog.selected_profile.clone(); + let metadata_cache = catalog + .ssh + .iter() + .find(|profile| profile.id == id) + .map(|profile| { + crate::client::endpoint::SshMetadataCache::new( + id.as_str(), + &profile.target, + &profile.session, + ) + }) + .transpose()?; if !catalog.remove_ssh(&id) { eprintln!("machine profile {id} was not found"); return Ok(1); } store_catalog(&catalog)?; + if let Some(cache) = metadata_cache { + cache.invalidate(); + } if catalog.selected_profile != previous_selection { catalog.store_selection().map_err(std::io::Error::other)?; } diff --git a/src/cli/status.rs b/src/cli/status.rs index d9c0be79..d13be784 100644 --- a/src/cli/status.rs +++ b/src/cli/status.rs @@ -174,7 +174,7 @@ fn print_server_status_body(server: &ServerRuntimeStatus, indent: &str) { } fn read_server_runtime_status() -> std::io::Result<ServerRuntimeStatus> { - match super::target::api_client()?.status() { + match super::target::server_status(&super::target::api_client()?) { Ok(status) => Ok(ServerRuntimeStatus::Running { version: status.version, protocol: status.protocol, diff --git a/src/cli/target.rs b/src/cli/target.rs index 64c072f9..220410f9 100644 --- a/src/cli/target.rs +++ b/src/cli/target.rs @@ -79,6 +79,7 @@ pub(super) fn api_client() -> io::Result<ApiClient> { target.profile.id.as_str(), &target.profile.target, &target.profile.session, + true, ) .map_err(|error| { io::Error::new( @@ -98,6 +99,51 @@ pub(super) fn api_client() -> io::Result<ApiClient> { }) } +pub(super) fn server_status( + client: &ApiClient, +) -> Result<crate::api::RuntimeStatus, crate::api::client::ApiClientError> { + let probe = || { + if is_remote() { + client.status_with_timeout(std::time::Duration::from_secs(15)) + } else { + client.status() + } + }; + let error = match probe() { + Ok(status) => return Ok(status), + Err(error) => error, + }; + // Only this read-only probe may rediscover and retry. Requests that follow + // the probe must never be replayed after an ambiguous SSH failure. + TARGET.with(|target| { + let mut target = target.borrow_mut(); + let Some(target) = target.as_mut() else { + return Err(error); + }; + let Some(bridge) = target.bridge.as_ref() else { + return Err(error); + }; + let Some(failure) = bridge.reported_failure() else { + return Err(error); + }; + if !bridge.used_cached_metadata + || !crate::remote::SavedSshApiBridge::stale_metadata_failure(&failure) + { + return Err(failure.into()); + } + bridge.invalidate_metadata(); + target.bridge.take(); + target.bridge = Some(crate::remote::SavedSshApiBridge::start( + target.profile.id.as_str(), + &target.profile.target, + &target.profile.session, + false, + )?); + Ok(()) + })?; + probe() +} + pub(super) fn remote_error(error: io::Error) -> io::Error { TARGET.with(|target| { let target = target.borrow(); |
