@@ -3226,66 +3226,92 @@ mod tests {
32263226 }
32273227
32283228 // tests around dropshot request timeouts during the blocking propolis setup
3229- #[ tokio :: test]
3230- async fn test_instance_create_timeout_while_starting_propolis ( ) {
3229+ #[ test]
3230+ fn test_instance_create_timeout_while_starting_propolis ( ) {
32313231 let logctx = omicron_test_utils:: dev:: test_setup_log (
32323232 "test_instance_create_timeout_while_starting_propolis" ,
32333233 ) ;
32343234 let log = logctx. log . new ( o ! ( FileKv ) ) ;
3235-
3236- let FakeNexusParts {
3237- nexus_client,
3238- state_rx,
3239- _dns_server,
3240- _nexus_server,
3241- } = FakeNexusParts :: new ( & log) . await ;
3242-
32433235 let temp_guard = Utf8TempDir :: new ( ) . unwrap ( ) ;
32443236
3245- let ( inst, _) = timeout (
3246- TIMEOUT_DURATION ,
3247- instance_struct (
3248- & log,
3249- // we want to test propolis not ever coming up
3250- SocketAddr :: V6 ( SocketAddrV6 :: new ( Ipv6Addr :: LOCALHOST , 1 , 0 , 0 ) ) ,
3237+ // Use a manual runtime so that `temp_guard` outlives it.
3238+ //
3239+ // The runner task's `setup_propolis_zone` calls `tokio::fs::write`
3240+ // (which uses `spawn_blocking`) to write zone config files. This
3241+ // test times out before the runner finishes, so the blocking write
3242+ // may still be in-flight when the test drops its locals. If
3243+ // `temp_guard` drops while the blocking thread is writing,
3244+ // `remove_dir_all` can race and fail silently, leaking files.
3245+ //
3246+ // Dropping the runtime first drains the blocking thread pool,
3247+ // ensuring the write completes before `temp_guard` cleans up.
3248+ //
3249+ // See: https://github.com/oxidecomputer/omicron/issues/10063
3250+ let rt = tokio:: runtime:: Builder :: new_current_thread ( )
3251+ . enable_all ( )
3252+ . build ( )
3253+ . unwrap ( ) ;
3254+
3255+ rt. block_on ( async {
3256+ let FakeNexusParts {
32513257 nexus_client,
3252- AvailableDatasetsReceiver :: fake_in_tempdir_for_tests (
3253- ZpoolOrRamdisk :: Ramdisk ,
3254- ) ,
3255- temp_guard. path ( ) . as_str ( ) ,
3256- ) ,
3257- )
3258- . await
3259- . expect ( "timed out creating Instance struct" ) ;
3258+ state_rx,
3259+ _dns_server,
3260+ _nexus_server,
3261+ } = FakeNexusParts :: new ( & log) . await ;
32603262
3261- let ( put_tx, put_rx) = oneshot:: channel ( ) ;
3263+ let ( inst, _) = timeout (
3264+ TIMEOUT_DURATION ,
3265+ instance_struct (
3266+ & log,
3267+ // we want to test propolis not ever coming up
3268+ SocketAddr :: V6 ( SocketAddrV6 :: new (
3269+ Ipv6Addr :: LOCALHOST ,
3270+ 1 ,
3271+ 0 ,
3272+ 0 ,
3273+ ) ) ,
3274+ nexus_client,
3275+ AvailableDatasetsReceiver :: fake_in_tempdir_for_tests (
3276+ ZpoolOrRamdisk :: Ramdisk ,
3277+ ) ,
3278+ temp_guard. path ( ) . as_str ( ) ,
3279+ ) ,
3280+ )
3281+ . await
3282+ . expect ( "timed out creating Instance struct" ) ;
32623283
3263- tokio :: time :: pause ( ) ;
3284+ let ( put_tx , put_rx ) = oneshot :: channel ( ) ;
32643285
3265- // pretending we're InstanceManager::ensure_state, try in vain to start
3266- // our "instance", but no propolis server is running
3267- inst. put_state ( put_tx, VmmStateRequested :: Running )
3268- . expect ( "failed to send Instance::put_state" ) ;
3286+ tokio:: time:: pause ( ) ;
32693287
3270- let timeout_fut = timeout ( TIMEOUT_DURATION , put_rx) ;
3288+ // pretending we're InstanceManager::ensure_state, try in vain
3289+ // to start our "instance", but no propolis server is running
3290+ inst. put_state ( put_tx, VmmStateRequested :: Running )
3291+ . expect ( "failed to send Instance::put_state" ) ;
32713292
3272- tokio :: time :: advance ( TIMEOUT_DURATION ) . await ;
3293+ let timeout_fut = timeout ( TIMEOUT_DURATION , put_rx ) ;
32733294
3274- tokio:: time:: resume ( ) ;
3295+ tokio:: time:: advance ( TIMEOUT_DURATION ) . await ;
32753296
3276- timeout_fut
3277- . await
3278- . expect_err ( "*should've* timed out waiting for Instance::put_state, but didn't?" ) ;
3297+ tokio:: time:: resume ( ) ;
32793298
3280- if let ReceivedInstanceState :: InstancePut ( SledVmmState {
3281- vmm_state : VmmRuntimeState { state : VmmState :: Running , .. } ,
3282- ..
3283- } ) = state_rx. borrow ( ) . to_owned ( )
3284- {
3285- panic ! (
3286- "Nexus's InstanceState should never have reached running if zone creation timed out"
3299+ timeout_fut. await . expect_err (
3300+ "*should've* timed out waiting for \
3301+ Instance::put_state, but didn't?",
32873302 ) ;
3288- }
3303+
3304+ if let ReceivedInstanceState :: InstancePut ( SledVmmState {
3305+ vmm_state : VmmRuntimeState { state : VmmState :: Running , .. } ,
3306+ ..
3307+ } ) = state_rx. borrow ( ) . to_owned ( )
3308+ {
3309+ panic ! (
3310+ "Nexus's InstanceState should never have reached \
3311+ running if zone creation timed out"
3312+ ) ;
3313+ }
3314+ } ) ;
32893315
32903316 logctx. cleanup_successful ( ) ;
32913317 }
0 commit comments