-
Notifications
You must be signed in to change notification settings - Fork 2
test(sentinel/checkpoint): isolate via CONTINUUM_CHECKPOINT_DIR env override #971
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -7,8 +7,17 @@ use std::path::PathBuf; | |||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| use super::types::{PipelineCheckpoint, PipelineStatus}; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Base directory for checkpoint storage | ||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Base directory for checkpoint storage. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Default: `~/.continuum/sentinel/checkpoints`. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Overridable via `CONTINUUM_CHECKPOINT_DIR` env var — used by tests to | ||||||||||||||||||||||||||||||||||||||||||||||||||
| /// isolate checkpoint state from the user's real `~/.continuum` (and to | ||||||||||||||||||||||||||||||||||||||||||||||||||
| /// survive the case where root-owned directories from a previous docker | ||||||||||||||||||||||||||||||||||||||||||||||||||
| /// container run leave the default path unwritable for the dev user). | ||||||||||||||||||||||||||||||||||||||||||||||||||
| fn checkpoints_dir() -> PathBuf { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if let Ok(override_dir) = std::env::var("CONTINUUM_CHECKPOINT_DIR") { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return PathBuf::from(override_dir); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| let home = dirs::home_dir().expect("Failed to resolve home directory"); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| home.join(".continuum").join("sentinel").join("checkpoints") | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -137,6 +146,35 @@ mod tests { | |||||||||||||||||||||||||||||||||||||||||||||||||
| use super::*; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| use crate::modules::sentinel::types::*; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| use std::collections::HashMap; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| use std::sync::OnceLock; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| use tempfile::TempDir; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Process-global tempdir for checkpoint tests, lazily initialized on | ||||||||||||||||||||||||||||||||||||||||||||||||||
| /// first access. All tests in this module share it — they use unique | ||||||||||||||||||||||||||||||||||||||||||||||||||
| /// UUID-derived handles, so file collisions don't happen. This isolates | ||||||||||||||||||||||||||||||||||||||||||||||||||
| /// the test runs from the user's real `~/.continuum/sentinel/checkpoints` | ||||||||||||||||||||||||||||||||||||||||||||||||||
| /// (which may be root-owned-and-unwritable on a dev box that previously | ||||||||||||||||||||||||||||||||||||||||||||||||||
| /// ran a docker container that mounted $HOME and chmod'd the dir under | ||||||||||||||||||||||||||||||||||||||||||||||||||
| /// root). | ||||||||||||||||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||||||||||||||||
| /// Stored in a static so the TempDir is dropped (and cleaned up) only | ||||||||||||||||||||||||||||||||||||||||||||||||||
| /// when the test process exits — a per-test TempDir would race with | ||||||||||||||||||||||||||||||||||||||||||||||||||
| /// cargo's default parallel test execution since `set_var` is process- | ||||||||||||||||||||||||||||||||||||||||||||||||||
| /// global. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| fn ensure_test_checkpoint_dir() { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| static TMPDIR: OnceLock<TempDir> = OnceLock::new(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| let dir = TMPDIR.get_or_init(|| { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| tempfile::tempdir().expect("Failed to create test checkpoint tempdir") | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // SAFETY: set_var is unsafe in newer Rust (process-global, racy with | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // other threads reading env). Tests in this module only ever write | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // the SAME path, so concurrent setters write the same value — race- | ||||||||||||||||||||||||||||||||||||||||||||||||||
| // free in practice. | ||||||||||||||||||||||||||||||||||||||||||||||||||
| std::env::set_var( | ||||||||||||||||||||||||||||||||||||||||||||||||||
| "CONTINUUM_CHECKPOINT_DIR", | ||||||||||||||||||||||||||||||||||||||||||||||||||
| dir.path(), | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+166
to
+176
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| let dir = TMPDIR.get_or_init(|| { | |
| tempfile::tempdir().expect("Failed to create test checkpoint tempdir") | |
| }); | |
| // SAFETY: set_var is unsafe in newer Rust (process-global, racy with | |
| // other threads reading env). Tests in this module only ever write | |
| // the SAME path, so concurrent setters write the same value — race- | |
| // free in practice. | |
| std::env::set_var( | |
| "CONTINUUM_CHECKPOINT_DIR", | |
| dir.path(), | |
| ); | |
| TMPDIR.get_or_init(|| { | |
| let dir = | |
| tempfile::tempdir().expect("Failed to create test checkpoint tempdir"); | |
| // SAFETY: set_var is process-global and must not race with other | |
| // writers. Performing it inside the OnceLock init closure ensures | |
| // only a single thread sets the test override for the lifetime of | |
| // this test process. | |
| std::env::set_var( | |
| "CONTINUUM_CHECKPOINT_DIR", | |
| dir.path(), | |
| ); | |
| dir | |
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
checkpoints_dir()usesstd::env::var, which requires UTF-8 and will also accept an empty string (mapping toPathBuf::from(""), i.e., the current working directory). Since this env var is intended to be a filesystem path override, consider usingstd::env::var_osand ignoring empty values so an accidentally-set empty/invalid override doesn’t redirect checkpoint writes into the process CWD.