aboutsummaryrefslogtreecommitdiff
path: root/src/cli
diff options
context:
space:
mode:
Diffstat (limited to 'src/cli')
-rw-r--r--src/cli/machine.rs34
-rw-r--r--src/cli/status.rs2
-rw-r--r--src/cli/target.rs46
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();