From 516b7afa08eb8463bec959ef6eafd12d87d74172 Mon Sep 17 00:00:00 2001 From: Alon Gubkin Date: Thu, 27 Aug 2026 00:09:13 -0700 Subject: [PATCH] fix: probe installed horizond during machine rejoin --- crates/alien-deploy-cli/src/commands/join.rs | 50 ++++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/crates/alien-deploy-cli/src/commands/join.rs b/crates/alien-deploy-cli/src/commands/join.rs index 3113a73f0..468c243ae 100644 --- a/crates/alien-deploy-cli/src/commands/join.rs +++ b/crates/alien-deploy-cli/src/commands/join.rs @@ -1207,7 +1207,8 @@ fn restore_credentials_and_service( } fn local_machine_connectivity(executable_path: &Path) -> Result> { - let output = Command::new(executable_path) + let status_executable = installed_status_executable(executable_path); + let output = Command::new(&status_executable) .args(["status", "--json"]) .output() .into_alien_error() @@ -1226,6 +1227,19 @@ fn local_machine_connectivity(executable_path: &Path) -> Result PathBuf { + let horizond = service_executable.with_file_name("horizond"); + if service_executable + .file_name() + .is_some_and(|name| name == "machine-entrypoint") + && horizond.is_file() + { + horizond + } else { + service_executable.to_path_buf() + } +} + async fn download_manifest(url: &str) -> Result { let response = reqwest::get(url) .await @@ -2457,6 +2471,8 @@ mod tests { use super::*; use flate2::{write::GzEncoder, Compression}; use std::io::Write; + #[cfg(unix)] + use std::os::unix::fs::PermissionsExt; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::TcpListener; @@ -2500,8 +2516,6 @@ mod tests { #[cfg(unix)] fn status_executable(directory: &Path, connectivity: &str) -> PathBuf { - use std::os::unix::fs::PermissionsExt; - let path = directory.join(format!("status-{connectivity}")); std::fs::write( &path, @@ -2534,6 +2548,36 @@ mod tests { ); } + #[test] + #[cfg(unix)] + fn local_status_bypasses_legacy_machine_entrypoint_wrapper() { + let directory = tempfile::tempdir().expect("status directory"); + let entrypoint = directory.path().join("machine-entrypoint"); + let wrapper_was_run = directory.path().join("wrapper-was-run"); + std::fs::write( + &entrypoint, + format!("#!/bin/sh\ntouch '{}'\nexit 1\n", wrapper_was_run.display()), + ) + .expect("write legacy wrapper"); + std::fs::set_permissions(&entrypoint, std::fs::Permissions::from_mode(0o755)) + .expect("make legacy wrapper executable"); + + let horizond = directory.path().join("horizond"); + std::fs::write( + &horizond, + "#!/bin/sh\nprintf '%s\\n' '{\"control\":{\"connectivity\":\"authenticationFailed\"}}'\n", + ) + .expect("write horizond status executable"); + std::fs::set_permissions(&horizond, std::fs::Permissions::from_mode(0o755)) + .expect("make horizond status executable"); + + assert_eq!( + local_machine_connectivity(&entrypoint).expect("authentication status"), + Some(LocalConnectivity::AuthenticationFailed) + ); + assert!(!wrapper_was_run.exists()); + } + #[tokio::test] async fn refreshed_credentials_wait_for_a_nonempty_machine_token() { let directory = tempfile::tempdir().expect("token directory");