From 06bce1c6b666a7549b2fc60b863b0e64c3551810 Mon Sep 17 00:00:00 2001 From: Alon Gubkin Date: Tue, 21 Jul 2026 16:10:42 +0300 Subject: [PATCH] fix: complete updates after resource removal --- crates/alien-deployment/src/updating.rs | 33 +++++++++--- .../alien-deployment/tests/test_platform.rs | 54 +++++++++++++++++++ 2 files changed, 79 insertions(+), 8 deletions(-) diff --git a/crates/alien-deployment/src/updating.rs b/crates/alien-deployment/src/updating.rs index 4dec5e090..3303de109 100644 --- a/crates/alien-deployment/src/updating.rs +++ b/crates/alien-deployment/src/updating.rs @@ -2,7 +2,8 @@ use crate::{ DeploymentConfig, DeploymentState, DeploymentStatus, DeploymentStepResult, ErrorData, Result, }; use alien_core::{ - ComputeClusterOutputs, Platform, ResourceLifecycle, Stack, StackState, StackStatus, + ComputeClusterOutputs, Platform, ResourceLifecycle, ResourceStatus, Stack, StackState, + StackStatus, }; use alien_error::{AlienError, Context}; use alien_infra::StackExecutor; @@ -19,6 +20,28 @@ fn machines_deployment_has_zero_machines(platform: Platform, stack_state: &Stack }) } +fn compute_update_status(stack_state: &StackState, target_stack: &Stack) -> Result { + let statuses = stack_state + .resources + .iter() + .filter_map(|(resource_id, resource)| { + (target_stack.resources.contains_key(resource_id) + || resource.status != ResourceStatus::Deleted) + .then_some(resource.status) + }) + .collect::>(); + + if statuses.is_empty() { + return Ok(StackStatus::Running); + } + + StackState::compute_stack_status_from_resources(&statuses).context( + ErrorData::StackExecutionFailed { + message: "Failed to compute update status".to_string(), + }, + ) +} + /// Handle UpdatePending → Updating transition /// /// This step: @@ -218,13 +241,7 @@ pub async fn handle_updating( })?; // Compute the stack status from the resulting state - let stack_status = - step_result - .next_state - .compute_stack_status() - .context(ErrorData::StackExecutionFailed { - message: "Failed to compute stack status".to_string(), - })?; + let stack_status = compute_update_status(&step_result.next_state, &target_stack)?; // Check if update is complete let waiting_for_machines = diff --git a/crates/alien-deployment/tests/test_platform.rs b/crates/alien-deployment/tests/test_platform.rs index 6b98dfcba..896a3d8b0 100644 --- a/crates/alien-deployment/tests/test_platform.rs +++ b/crates/alien-deployment/tests/test_platform.rs @@ -777,6 +777,60 @@ async fn setup_authorized_update_clears_authority_only_on_success() { ); } +#[tokio::test] +async fn update_completes_after_removed_resource_is_deleted() { + let config = create_test_config("hash_v1", false); + let mut stack_v1 = create_test_stack("test-stack", "function-a"); + let function_b = Worker::new("function-b".to_string()) + .code(WorkerCode::Image { + image: "test:latest".to_string(), + }) + .permissions("default".to_string()) + .build(); + stack_v1.resources.insert( + "function-b".to_string(), + ResourceEntry { + config: alien_core::Resource::new(function_b), + lifecycle: ResourceLifecycle::Live, + dependencies: Vec::new(), + remote_access: false, + }, + ); + + let mut state = run_to_completion(create_initial_state(stack_v1), config.clone()).await; + let stack_v2 = create_test_stack("test-stack", "function-a"); + start_update( + &mut state, + ReleaseInfo { + release_id: Some("rel_v2".to_string()), + version: Some("2.0.0".to_string()), + description: None, + stack: stack_v2, + }, + ); + + let completed = run_to_completion(state, config).await; + + assert_eq!(completed.status, DeploymentStatus::Running); + assert_eq!( + completed + .stack_state + .as_ref() + .unwrap() + .resources + .get("function-b") + .unwrap() + .status, + alien_core::ResourceStatus::Deleted, + "the executor's deletion tombstone should be preserved" + ); + assert_eq!( + completed.current_release.as_ref().unwrap().release_id, + Some("rel_v2".to_string()) + ); + assert!(completed.target_release.is_none()); +} + #[tokio::test] async fn stale_waiting_for_machines_returns_to_updating() { let stack_v1 = create_test_stack("test-stack", "test-function");