@@ -654,6 +654,25 @@ impl fmt::Debug for ComputeRuntime {
654654 }
655655}
656656
657+ #[ derive( Clone , Copy ) ]
658+ enum SupervisorSessionStateUpdate < ' a > {
659+ Connected {
660+ instance_id : & ' a str ,
661+ admission : Option < & ' a openshell_core:: proto:: SandboxConfigurationAdmission > ,
662+ readiness : SupervisorRuntimeReadiness ,
663+ } ,
664+ Disconnected {
665+ terminal_delivery_finalized : bool ,
666+ } ,
667+ }
668+
669+ #[ derive( Clone , Copy ) ]
670+ enum SupervisorRuntimeReadiness {
671+ Initializing ,
672+ Ready ,
673+ ReadyAfterAdmission ,
674+ }
675+
657676impl ComputeRuntime {
658677 #[ allow( clippy:: too_many_arguments) ]
659678 #[ tracing:: instrument(
@@ -1610,7 +1629,7 @@ impl ComputeRuntime {
16101629 ) -> Option < Sandbox > {
16111630 let sandbox_id = transition. object_id ( ) . to_string ( ) ;
16121631 let expected_resource_version = sandbox_resource_version ( transition) ;
1613- let session_connected = self . supervisor_sessions . has_session ( & sandbox_id) ;
1632+ let session_connected = self . supervisor_sessions . is_runtime_ready ( & sandbox_id) ;
16141633 match self
16151634 . store
16161635 . update_message_cas :: < Sandbox , _ > ( & sandbox_id, expected_resource_version, |sandbox| {
@@ -2100,7 +2119,7 @@ impl ComputeRuntime {
21002119
21012120 match observed {
21022121 Ok ( Some ( snapshot) ) if snapshot. id == sandbox_id && snapshot. status . is_some ( ) => {
2103- let session_connected = self . supervisor_sessions . has_session ( sandbox_id) ;
2122+ let session_connected = self . supervisor_sessions . is_runtime_ready ( sandbox_id) ;
21042123 self . write_delete_recovery_with_retry (
21052124 sandbox_id,
21062125 deleting_resource_version,
@@ -3312,7 +3331,7 @@ impl ComputeRuntime {
33123331 expected_resource_version : u64 ,
33133332 existing_phase : SandboxPhase ,
33143333 ) -> Result < ( ) , String > {
3315- let session_connected = self . supervisor_sessions . has_session ( & incoming. id ) ;
3334+ let session_connected = self . supervisor_sessions . is_runtime_ready ( & incoming. id ) ;
33163335 let sandbox = self
33173336 . store
33183337 . update_message_cas :: < Sandbox , _ > (
@@ -3354,8 +3373,31 @@ impl ComputeRuntime {
33543373 sandbox_id : & str ,
33553374 instance_id : & str ,
33563375 ) -> Result < ( ) , String > {
3357- self . set_supervisor_session_state ( sandbox_id, true , Some ( instance_id) , None , false )
3358- . await
3376+ self . set_supervisor_session_state (
3377+ sandbox_id,
3378+ SupervisorSessionStateUpdate :: Connected {
3379+ instance_id,
3380+ admission : None ,
3381+ readiness : SupervisorRuntimeReadiness :: Ready ,
3382+ } ,
3383+ )
3384+ . await
3385+ }
3386+
3387+ pub async fn supervisor_runtime_ready (
3388+ & self ,
3389+ sandbox_id : & str ,
3390+ instance_id : & str ,
3391+ ) -> Result < ( ) , String > {
3392+ self . set_supervisor_session_state (
3393+ sandbox_id,
3394+ SupervisorSessionStateUpdate :: Connected {
3395+ instance_id,
3396+ admission : None ,
3397+ readiness : SupervisorRuntimeReadiness :: ReadyAfterAdmission ,
3398+ } ,
3399+ )
3400+ . await
33593401 }
33603402
33613403 pub async fn supervisor_session_admission (
@@ -3366,10 +3408,15 @@ impl ComputeRuntime {
33663408 ) -> Result < ( ) , String > {
33673409 self . set_supervisor_session_state (
33683410 sandbox_id,
3369- true ,
3370- Some ( instance_id) ,
3371- Some ( admission) ,
3372- false ,
3411+ SupervisorSessionStateUpdate :: Connected {
3412+ instance_id,
3413+ admission : Some ( admission) ,
3414+ readiness : if self . supervisor_sessions . is_runtime_ready ( sandbox_id) {
3415+ SupervisorRuntimeReadiness :: Ready
3416+ } else {
3417+ SupervisorRuntimeReadiness :: Initializing
3418+ } ,
3419+ } ,
33733420 )
33743421 . await
33753422 }
@@ -3381,54 +3428,72 @@ impl ComputeRuntime {
33813428 ) -> Result < ( ) , String > {
33823429 self . set_supervisor_session_state (
33833430 sandbox_id,
3384- false ,
3385- None ,
3386- None ,
3387- terminal_delivery_finalized,
3431+ SupervisorSessionStateUpdate :: Disconnected {
3432+ terminal_delivery_finalized,
3433+ } ,
33883434 )
33893435 . await
33903436 }
33913437
33923438 async fn set_supervisor_session_state (
33933439 & self ,
33943440 sandbox_id : & str ,
3395- connected : bool ,
3396- instance_id : Option < & str > ,
3397- admission : Option < & openshell_core:: proto:: SandboxConfigurationAdmission > ,
3398- terminal_delivery_finalized : bool ,
3441+ update : SupervisorSessionStateUpdate < ' _ > ,
33993442 ) -> Result < ( ) , String > {
34003443 let _guard = self . sync_lock . lock ( ) . await ;
34013444 let existing = self
34023445 . store
34033446 . get_message :: < Sandbox > ( sandbox_id)
34043447 . await
34053448 . map_err ( |err| err. to_string ( ) ) ?;
3406- self . set_supervisor_session_state_from_snapshot (
3407- sandbox_id,
3408- connected,
3409- instance_id,
3410- admission,
3411- terminal_delivery_finalized,
3412- existing,
3413- )
3414- . await
3449+ self . set_supervisor_session_state_from_snapshot ( sandbox_id, update, existing)
3450+ . await
34153451 }
34163452
34173453 async fn set_supervisor_session_state_from_snapshot (
34183454 & self ,
34193455 sandbox_id : & str ,
3420- connected : bool ,
3421- instance_id : Option < & str > ,
3422- admission : Option < & openshell_core:: proto:: SandboxConfigurationAdmission > ,
3423- terminal_delivery_finalized : bool ,
3456+ update : SupervisorSessionStateUpdate < ' _ > ,
34243457 mut existing : Option < Sandbox > ,
34253458 ) -> Result < ( ) , String > {
3459+ let ( connected, instance_id, admission, terminal_delivery_finalized, readiness) =
3460+ match update {
3461+ SupervisorSessionStateUpdate :: Connected {
3462+ instance_id,
3463+ admission,
3464+ readiness,
3465+ } => ( true , Some ( instance_id) , admission, false , Some ( readiness) ) ,
3466+ SupervisorSessionStateUpdate :: Disconnected {
3467+ terminal_delivery_finalized,
3468+ } => ( false , None , None , terminal_delivery_finalized, None ) ,
3469+ } ;
3470+ let runtime_ready = matches ! (
3471+ readiness,
3472+ Some (
3473+ SupervisorRuntimeReadiness :: Ready | SupervisorRuntimeReadiness :: ReadyAfterAdmission
3474+ )
3475+ ) ;
3476+ let require_activated_configuration = matches ! (
3477+ readiness,
3478+ Some ( SupervisorRuntimeReadiness :: ReadyAfterAdmission )
3479+ ) ;
34263480 for attempt in 1 ..=SUPERVISOR_SESSION_CAS_RETRY_LIMIT {
34273481 let Some ( current) = existing else {
34283482 return Ok ( ( ) ) ;
34293483 } ;
34303484 let current_phase =
34313485 SandboxPhase :: try_from ( current. phase ( ) ) . unwrap_or ( SandboxPhase :: Unknown ) ;
3486+ if connected
3487+ && runtime_ready
3488+ && require_activated_configuration
3489+ && current
3490+ . status
3491+ . as_ref ( )
3492+ . and_then ( |status| status. configuration_activated )
3493+ != Some ( true )
3494+ {
3495+ return Err ( "sandbox configuration is not durably admitted" . to_string ( ) ) ;
3496+ }
34323497 if connected
34333498 && ( provisioning_deadline:: timed_out ( & current)
34343499 || matches ! (
@@ -3468,7 +3533,6 @@ impl ComputeRuntime {
34683533 expected_resource_version,
34693534 |sandbox| {
34703535 if connected {
3471- ensure_supervisor_ready_status ( & mut sandbox. status ) ;
34723536 let status = sandbox. status . get_or_insert_with ( Default :: default) ;
34733537 status. main_process_instance_id =
34743538 instance_id. unwrap_or_default ( ) . to_string ( ) ;
@@ -3480,7 +3544,10 @@ impl ComputeRuntime {
34803544 == i32:: from ( openshell_core:: proto:: ConfigurationAdmissionState :: Accepted ) ,
34813545 ) ;
34823546 }
3483- sandbox. set_phase ( SandboxPhase :: Ready as i32 ) ;
3547+ if runtime_ready {
3548+ ensure_supervisor_ready_status ( & mut sandbox. status ) ;
3549+ sandbox. set_phase ( SandboxPhase :: Ready as i32 ) ;
3550+ }
34843551 } else {
34853552 ensure_supervisor_not_ready_status ( & mut sandbox. status ) ;
34863553 sandbox. set_phase ( SandboxPhase :: Provisioning as i32 ) ;
@@ -10541,6 +10608,13 @@ mod tests {
1054110608 rejected. status. as_ref( ) . unwrap( ) . configuration_activated,
1054210609 Some ( false )
1054310610 ) ;
10611+ assert ! (
10612+ runtime
10613+ . supervisor_runtime_ready( "sb-1" , "supervisor-1" )
10614+ . await
10615+ . unwrap_err( )
10616+ . contains( "not durably admitted" )
10617+ ) ;
1054410618
1054510619 admission. state = ConfigurationAdmissionState :: Accepted . into ( ) ;
1054610620 admission. error . clear ( ) ;
@@ -10554,7 +10628,7 @@ mod tests {
1055410628 . await
1055510629 . unwrap ( )
1055610630 . unwrap ( ) ;
10557- assert_eq ! ( repaired. phase( ) , SandboxPhase :: Ready as i32 ) ;
10631+ assert_eq ! ( repaired. phase( ) , SandboxPhase :: Provisioning as i32 ) ;
1055810632 assert_eq ! (
1055910633 repaired. status. as_ref( ) . unwrap( ) . main_process_instance_id,
1056010634 "supervisor-1"
@@ -10563,6 +10637,18 @@ mod tests {
1056310637 repaired. status. as_ref( ) . unwrap( ) . configuration_activated,
1056410638 Some ( true )
1056510639 ) ;
10640+
10641+ runtime
10642+ . supervisor_runtime_ready ( "sb-1" , "supervisor-1" )
10643+ . await
10644+ . unwrap ( ) ;
10645+ let ready = runtime
10646+ . store
10647+ . get_message :: < Sandbox > ( "sb-1" )
10648+ . await
10649+ . unwrap ( )
10650+ . unwrap ( ) ;
10651+ assert_eq ! ( ready. phase( ) , SandboxPhase :: Ready as i32 ) ;
1056610652 }
1056710653
1056810654 #[ tokio:: test]
@@ -10604,10 +10690,11 @@ mod tests {
1060410690 runtime
1060510691 . set_supervisor_session_state_from_snapshot (
1060610692 "sb-1" ,
10607- true ,
10608- Some ( "test-generation" ) ,
10609- None ,
10610- false ,
10693+ SupervisorSessionStateUpdate :: Connected {
10694+ instance_id : "test-generation" ,
10695+ admission : None ,
10696+ readiness : SupervisorRuntimeReadiness :: Ready ,
10697+ } ,
1061110698 stale,
1061210699 )
1061310700 . await
0 commit comments