@@ -363,6 +363,9 @@ pub async fn run_sandbox(
363363 . as_ref ( )
364364 . map ( |connection| connection. writer . clone ( ) ) ;
365365 let process_exit_ack = Arc :: new ( tokio:: sync:: Mutex :: new ( None ) ) ;
366+ let initial_provider_env_generation = sidecar_bootstrap
367+ . as_ref ( )
368+ . map_or ( 0 , |bootstrap| bootstrap. provider_env_generation ) ;
366369 let mut process_control_closed = None ;
367370 if let Some ( connection) = process_control_connection {
368371 process_control_closed = Some ( connection. closed ) ;
@@ -371,6 +374,7 @@ pub async fn run_sandbox(
371374 provider_credentials. clone ( ) ,
372375 agent_proposals. clone ( ) ,
373376 Arc :: clone ( & process_exit_ack) ,
377+ initial_provider_env_generation,
374378 ) ;
375379 }
376380
@@ -569,6 +573,7 @@ pub async fn run_sandbox(
569573 sidecar_control:: BootstrapData {
570574 policy_proto : proto. clone ( ) ,
571575 provider_env_revision : provider_credentials. snapshot ( ) . revision ,
576+ provider_env_generation : 0 ,
572577 provider_child_env : provider_env. clone ( ) ,
573578 agent_proposals_enabled : agent_proposals. enabled ( ) ,
574579 proxy_ca_cert_path : ca_paths. as_ref ( ) . map ( |paths| paths. 0 . clone ( ) ) ,
@@ -1072,25 +1077,29 @@ fn spawn_sidecar_control_update_watcher(
10721077 provider_credentials : ProviderCredentialState ,
10731078 agent_proposals : AgentProposals ,
10741079 exit_ack : MainProcessExitAckWaiter ,
1080+ mut provider_env_generation : u64 ,
10751081) -> tokio:: task:: JoinHandle < ( ) > {
10761082 tokio:: spawn ( async move {
10771083 while let Some ( update) = updates. recv ( ) . await {
10781084 match update {
10791085 sidecar_control:: ControlUpdate :: ProviderEnv {
10801086 revision,
1087+ generation,
10811088 provider_child_env,
10821089 } => {
1083- if revision <= provider_credentials . snapshot ( ) . revision {
1090+ if generation <= provider_env_generation {
10841091 continue ;
10851092 }
10861093 let env_count = provider_credentials
10871094 . install_child_env_snapshot ( revision, provider_child_env) ;
1095+ provider_env_generation = generation;
10881096 ocsf_emit ! (
10891097 ConfigStateChangeBuilder :: new( ocsf_ctx( ) )
10901098 . severity( SeverityId :: Informational )
10911099 . status( StatusId :: Success )
10921100 . state( StateId :: Enabled , "loaded" )
10931101 . unmapped( "provider_env_revision" , serde_json:: json!( revision) )
1102+ . unmapped( "provider_env_generation" , serde_json:: json!( generation) )
10941103 . message( format!(
10951104 "Sidecar provider environment refreshed [revision:{revision} env_count:{env_count}]"
10961105 ) )
@@ -4350,21 +4359,24 @@ mod tests {
43504359 }
43514360
43524361 #[ tokio:: test]
4353- async fn sidecar_control_provider_env_update_installs_newer_revision ( ) {
4362+ async fn sidecar_control_provider_env_update_orders_by_generation ( ) {
43544363 let ( tx, rx) = tokio:: sync:: mpsc:: unbounded_channel ( ) ;
43554364 let provider_credentials = ProviderCredentialState :: from_child_env_snapshot (
4356- 1 ,
4365+ u64 :: MAX ,
43574366 std:: collections:: HashMap :: from ( [ ( "TOKEN" . to_string ( ) , "old" . to_string ( ) ) ] ) ,
43584367 ) ;
4368+ let agent_proposals = AgentProposals :: new ( true ) ;
43594369 let handle = spawn_sidecar_control_update_watcher (
43604370 rx,
43614371 provider_credentials. clone ( ) ,
4362- AgentProposals :: default ( ) ,
4372+ agent_proposals . clone ( ) ,
43634373 Arc :: new ( tokio:: sync:: Mutex :: new ( None ) ) ,
4374+ 10 ,
43644375 ) ;
43654376
43664377 tx. send ( sidecar_control:: ControlUpdate :: ProviderEnv {
4367- revision : 2 ,
4378+ revision : 1 ,
4379+ generation : 11 ,
43684380 provider_child_env : std:: collections:: HashMap :: from ( [ (
43694381 "TOKEN" . to_string ( ) ,
43704382 "new" . to_string ( ) ,
@@ -4374,7 +4386,7 @@ mod tests {
43744386
43754387 timeout ( Duration :: from_secs ( 1 ) , async {
43764388 loop {
4377- if provider_credentials. snapshot ( ) . revision == 2 {
4389+ if provider_credentials. snapshot ( ) . revision == 1 {
43784390 break ;
43794391 }
43804392 tokio:: time:: sleep ( Duration :: from_millis ( 10 ) ) . await ;
@@ -4383,21 +4395,33 @@ mod tests {
43834395 . await
43844396 . unwrap ( ) ;
43854397 let snapshot = provider_credentials. snapshot ( ) ;
4386- assert_eq ! ( snapshot. revision, 2 ) ;
4398+ assert_eq ! ( snapshot. revision, 1 ) ;
43874399 assert_eq ! (
43884400 snapshot. child_env. get( "TOKEN" ) . map( String :: as_str) ,
43894401 Some ( "new" )
43904402 ) ;
43914403
43924404 tx. send ( sidecar_control:: ControlUpdate :: ProviderEnv {
4393- revision : 1 ,
4405+ revision : 2 ,
4406+ generation : 11 ,
43944407 provider_child_env : std:: collections:: HashMap :: from ( [ (
43954408 "TOKEN" . to_string ( ) ,
4396- "stale " . to_string ( ) ,
4409+ "duplicate-generation " . to_string ( ) ,
43974410 ) ] ) ,
43984411 } )
43994412 . unwrap ( ) ;
4400- tokio:: time:: sleep ( Duration :: from_millis ( 20 ) ) . await ;
4413+ tx. send ( sidecar_control:: ControlUpdate :: AgentProposals {
4414+ enabled : false ,
4415+ config_revision : 1 ,
4416+ } )
4417+ . unwrap ( ) ;
4418+ timeout ( Duration :: from_secs ( 1 ) , async {
4419+ while agent_proposals. enabled ( ) {
4420+ tokio:: time:: sleep ( Duration :: from_millis ( 10 ) ) . await ;
4421+ }
4422+ } )
4423+ . await
4424+ . unwrap ( ) ;
44014425 assert_eq ! (
44024426 provider_credentials
44034427 . snapshot( )
@@ -4406,6 +4430,54 @@ mod tests {
44064430 . map( String :: as_str) ,
44074431 Some ( "new" )
44084432 ) ;
4433+
4434+ tx. send ( sidecar_control:: ControlUpdate :: ProviderEnv {
4435+ revision : 2 ,
4436+ generation : 12 ,
4437+ provider_child_env : std:: collections:: HashMap :: from ( [ (
4438+ "TOKEN" . to_string ( ) ,
4439+ "newest" . to_string ( ) ,
4440+ ) ] ) ,
4441+ } )
4442+ . unwrap ( ) ;
4443+ timeout ( Duration :: from_secs ( 1 ) , async {
4444+ loop {
4445+ if provider_credentials. snapshot ( ) . revision == 2 {
4446+ break ;
4447+ }
4448+ tokio:: time:: sleep ( Duration :: from_millis ( 10 ) ) . await ;
4449+ }
4450+ } )
4451+ . await
4452+ . unwrap ( ) ;
4453+
4454+ tx. send ( sidecar_control:: ControlUpdate :: ProviderEnv {
4455+ revision : u64:: MAX ,
4456+ generation : 11 ,
4457+ provider_child_env : std:: collections:: HashMap :: from ( [ (
4458+ "TOKEN" . to_string ( ) ,
4459+ "stale" . to_string ( ) ,
4460+ ) ] ) ,
4461+ } )
4462+ . unwrap ( ) ;
4463+ tx. send ( sidecar_control:: ControlUpdate :: AgentProposals {
4464+ enabled : true ,
4465+ config_revision : 2 ,
4466+ } )
4467+ . unwrap ( ) ;
4468+ timeout ( Duration :: from_secs ( 1 ) , async {
4469+ while !agent_proposals. enabled ( ) {
4470+ tokio:: time:: sleep ( Duration :: from_millis ( 10 ) ) . await ;
4471+ }
4472+ } )
4473+ . await
4474+ . unwrap ( ) ;
4475+ let snapshot = provider_credentials. snapshot ( ) ;
4476+ assert_eq ! ( snapshot. revision, 2 ) ;
4477+ assert_eq ! (
4478+ snapshot. child_env. get( "TOKEN" ) . map( String :: as_str) ,
4479+ Some ( "newest" )
4480+ ) ;
44094481 handle. abort ( ) ;
44104482 }
44114483
@@ -4420,6 +4492,7 @@ mod tests {
44204492 provider_credentials,
44214493 agent_proposals. clone ( ) ,
44224494 Arc :: new ( tokio:: sync:: Mutex :: new ( None ) ) ,
4495+ 0 ,
44234496 ) ;
44244497
44254498 tx. send ( sidecar_control:: ControlUpdate :: AgentProposals {
0 commit comments