From 329efa4848ecd5b3fe816c82f95df24ae81fe3b2 Mon Sep 17 00:00:00 2001 From: Alon Gubkin Date: Sun, 30 Aug 2026 01:51:30 -0700 Subject: [PATCH] fix: retry hosted compute updates after failed apply --- crates/alien-deploy-cli/src/commands/up.rs | 37 +++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/crates/alien-deploy-cli/src/commands/up.rs b/crates/alien-deploy-cli/src/commands/up.rs index 8e0e474f8..2e95fa42a 100644 --- a/crates/alien-deploy-cli/src/commands/up.rs +++ b/crates/alien-deploy-cli/src/commands/up.rs @@ -393,6 +393,26 @@ mod tests { } } + #[test] + fn hosted_compute_retry_resubmits_an_equal_persisted_target() { + assert!(!hosted_compute_update_required("running", false)); + assert!(hosted_compute_update_required("running", true)); + + for status in [ + "update-pending", + "updating", + "update-failed", + "refresh-failed", + "initial-setup", + "initial-setup-failed", + "provisioning", + "waiting-for-machines", + "provisioning-failed", + ] { + assert!(hosted_compute_update_required(status, false), "{status}"); + } + } + #[test] fn stable_channel_accepts_exact_semver_tag() { assert_eq!( @@ -1403,7 +1423,10 @@ pub async fn up_command(args: UpArgs, embedded_config: Option<&DeployCliConfig>) .to_string(), })); } - if current_stack_settings.compute != stack_settings.compute { + if hosted_compute_update_required( + ¤t_deployment.status, + current_stack_settings.compute != stack_settings.compute, + ) { update_hosted_compute_settings( &resolved.base_url, &effective_token, @@ -2785,6 +2808,18 @@ fn supports_hosted_compute_update(status: &str) -> bool { ) } +/// Whether the hosted platform must receive the requested compute target. +/// +/// Platform persists the desired settings before the deployment engine applies +/// them. A retryable or in-flight lifecycle can therefore report settings equal +/// to the caller's request even though the cloud still has the previous +/// capacity. Only a fully running deployment can safely treat equality as a +/// no-op; every other supported lifecycle must re-submit the target and let the +/// platform operation queue handle it idempotently. +fn hosted_compute_update_required(status: &str, compute_changed: bool) -> bool { + compute_changed || status != "running" +} + async fn update_hosted_compute_settings( base_url: &str, token: &str,