From cbcc0dad628d14b7c828431a47f47e23435c82e0 Mon Sep 17 00:00:00 2001 From: Bryan Helmkamp Date: Wed, 8 Apr 2026 15:48:51 -0400 Subject: [PATCH] fix(test): eliminate session lock race and openssl /dev/stdin flake Two flake sources identified across 100+ full-suite runs: 1. Session lock EINVAL race: cleanup_session_root's remove_dir_all could delete the session root between with_session_lock's create_dir_all and File::create, causing EINVAL. Fix: retry the create-dir + create-file sequence as a unit. 2. mTLS cert generation: openssl req -key /dev/stdin failed under fd pressure with "Bad file descriptor". Fix: read from the already- written server.key file path instead of piping through /dev/stdin. Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/crates/fabro-cli/src/commands/install.rs | 7 ++++--- lib/crates/fabro-test/src/lib.rs | 20 +++++++++++++++----- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/lib/crates/fabro-cli/src/commands/install.rs b/lib/crates/fabro-cli/src/commands/install.rs index 39f3d6a4b..a425daacc 100644 --- a/lib/crates/fabro-cli/src/commands/install.rs +++ b/lib/crates/fabro-cli/src/commands/install.rs @@ -139,16 +139,17 @@ fn generate_mtls_certs(dir: &Path) -> Result<()> { let server_key_path = dir.join("server.key"); std::fs::write(&server_key_path, &server_key)?; - let csr = run_openssl_with_stdin( + let csr = run_openssl( &[ "req", "-new", "-key", - "/dev/stdin", + server_key_path + .to_str() + .context("server key path is not valid UTF-8")?, "-subj", "/CN=localhost", ], - &server_key, "generate server CSR", )?; diff --git a/lib/crates/fabro-test/src/lib.rs b/lib/crates/fabro-test/src/lib.rs index 28aab0c78..44efc1c0b 100644 --- a/lib/crates/fabro-test/src/lib.rs +++ b/lib/crates/fabro-test/src/lib.rs @@ -262,13 +262,23 @@ fn ensure_parent_dir(path: &Path) { } fn with_session_lock(root: &Path, f: impl FnOnce() -> T) -> T { - std::fs::create_dir_all(root) - .unwrap_or_else(|err| panic!("failed to create {}: {err}", root.display())); let lock_path = session_lock_path(root); - ensure_parent_dir(&lock_path); - let lock_file = File::create(&lock_path) - .unwrap_or_else(|err| panic!("failed to create {}: {err}", lock_path.display())); + // Retry create-dir + create-file as a unit: another process's + // cleanup_session_root can remove_dir_all between the two calls. let deadline = std::time::Instant::now() + SESSION_LOCK_TIMEOUT; + let lock_file = loop { + std::fs::create_dir_all(root) + .unwrap_or_else(|err| panic!("failed to create {}: {err}", root.display())); + ensure_parent_dir(&lock_path); + match File::create(&lock_path) { + Ok(f) => break f, + Err(err) if std::time::Instant::now() < deadline => { + std::thread::sleep(Duration::from_millis(10)); + continue; + } + Err(err) => panic!("failed to create {}: {err}", lock_path.display()), + } + }; while !fabro_proc::try_flock_exclusive(&lock_file) .unwrap_or_else(|err| panic!("failed to lock {}: {err}", lock_path.display())) {