From 10a5bc0280a86082a7491a43d9ec0fe16cd1fad6 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Wed, 11 Mar 2026 15:17:53 -0600 Subject: [PATCH] fix: add restart_again flag to DesiredStatus::Restarting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a restart is requested while the service is already restarting (stopped but not yet started), set restart_again so the actor will perform another stop→start cycle after the current one completes. --- core/src/service/effects/control.rs | 2 +- core/src/service/service_actor.rs | 4 +- core/src/service/service_map.rs | 7 +--- core/src/status/mod.rs | 40 ++++++++++++++----- sdk/base/lib/osBindings/DesiredStatus.ts | 2 +- .../services/api/embassy-mock-api.service.ts | 2 +- 6 files changed, 37 insertions(+), 20 deletions(-) diff --git a/core/src/service/effects/control.rs b/core/src/service/effects/control.rs index edaf998e8..4333cf907 100644 --- a/core/src/service/effects/control.rs +++ b/core/src/service/effects/control.rs @@ -163,7 +163,7 @@ pub async fn set_main_status( if prev.is_none() && status == SetMainStatusStatus::Running { s.as_desired_mut().map_mutate(|s| { Ok(match s { - DesiredStatus::Restarting => DesiredStatus::Running, + DesiredStatus::Restarting { .. } => DesiredStatus::Running, x => x, }) })?; diff --git a/core/src/service/service_actor.rs b/core/src/service/service_actor.rs index ed8feafdf..cac038cb3 100644 --- a/core/src/service/service_actor.rs +++ b/core/src/service/service_actor.rs @@ -103,7 +103,7 @@ async fn service_actor_loop<'a>( match status { StatusInfo { - desired: DesiredStatus::Running | DesiredStatus::Restarting, + desired: DesiredStatus::Running | DesiredStatus::Restarting { .. }, started: None, .. } => { @@ -114,7 +114,7 @@ async fn service_actor_loop<'a>( } StatusInfo { desired: - DesiredStatus::Stopped | DesiredStatus::Restarting | DesiredStatus::BackingUp { .. }, + DesiredStatus::Stopped | DesiredStatus::Restarting { .. } | DesiredStatus::BackingUp { .. }, started: Some(_), .. } => { diff --git a/core/src/service/service_map.rs b/core/src/service/service_map.rs index 5c657d562..5668b4094 100644 --- a/core/src/service/service_map.rs +++ b/core/src/service/service_map.rs @@ -243,12 +243,7 @@ impl ServiceMap { PackageState::Installing(installing) }, s9pk: installed_path, - status_info: StatusInfo { - error: None, - health: BTreeMap::new(), - started: None, - desired: DesiredStatus::Stopped, - }, + status_info: StatusInfo::default(), registry, developer_key: Pem::new(developer_key), icon, diff --git a/core/src/status/mod.rs b/core/src/status/mod.rs index ea4d0da98..a9e23cdb2 100644 --- a/core/src/status/mod.rs +++ b/core/src/status/mod.rs @@ -38,7 +38,17 @@ impl Model { .map_mutate(|s| Ok(Some(s.unwrap_or_else(|| Utc::now()))))?; self.as_desired_mut().map_mutate(|s| { Ok(match s { - DesiredStatus::Restarting => DesiredStatus::Running, + DesiredStatus::Restarting { + restart_again: true, + } => { + // Clear the flag but stay Restarting so actor will stop→start again + DesiredStatus::Restarting { + restart_again: false, + } + } + DesiredStatus::Restarting { + restart_again: false, + } => DesiredStatus::Running, a => a, }) })?; @@ -55,7 +65,9 @@ impl Model { Ok(()) } pub fn restart(&mut self) -> Result<(), Error> { - self.as_desired_mut().map_mutate(|s| Ok(s.restart()))?; + let started = self.as_started().transpose_ref().is_some(); + self.as_desired_mut() + .map_mutate(|s| Ok(s.restart(started)))?; self.as_health_mut().ser(&Default::default())?; Ok(()) } @@ -69,7 +81,7 @@ impl Model { DesiredStatus::BackingUp { on_complete: StartStop::Stop, } => DesiredStatus::Stopped, - DesiredStatus::Restarting => DesiredStatus::Running, + DesiredStatus::Restarting { .. } => DesiredStatus::Running, x => x, }) })?; @@ -84,9 +96,14 @@ impl Model { #[serde(rename_all_fields = "camelCase")] pub enum DesiredStatus { Stopped, - Restarting, + Restarting { + #[serde(default)] + restart_again: bool, + }, Running, - BackingUp { on_complete: StartStop }, + BackingUp { + on_complete: StartStop, + }, } impl Default for DesiredStatus { fn default() -> Self { @@ -97,7 +114,7 @@ impl DesiredStatus { pub fn running(&self) -> bool { match self { Self::Running - | Self::Restarting + | Self::Restarting { .. } | Self::BackingUp { on_complete: StartStop::Start, } => true, @@ -140,10 +157,15 @@ impl DesiredStatus { } } - pub fn restart(&self) -> Self { + pub fn restart(&self, started: bool) -> Self { match self { - Self::Running => Self::Restarting, - x => *x, // no-op: restart is meaningless in any other state + Self::Running => Self::Restarting { + restart_again: false, + }, + Self::Restarting { .. } if !started => Self::Restarting { + restart_again: true, + }, + x => *x, } } } diff --git a/sdk/base/lib/osBindings/DesiredStatus.ts b/sdk/base/lib/osBindings/DesiredStatus.ts index 72411a339..56afa2bca 100644 --- a/sdk/base/lib/osBindings/DesiredStatus.ts +++ b/sdk/base/lib/osBindings/DesiredStatus.ts @@ -3,6 +3,6 @@ import type { StartStop } from './StartStop' export type DesiredStatus = | { main: 'stopped' } - | { main: 'restarting' } + | { main: 'restarting'; restartAgain: boolean } | { main: 'running' } | { main: 'backing-up'; onComplete: StartStop } diff --git a/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts b/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts index 10c2ed162..9f07c5426 100644 --- a/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts +++ b/web/projects/ui/src/app/services/api/embassy-mock-api.service.ts @@ -1289,7 +1289,7 @@ export class MockApiService extends ApiService { op: PatchOp.REPLACE, path, value: { - desired: { main: 'restarting' }, + desired: { main: 'restarting', restartAgain: false }, started: null, error: null, health: {},