mirror of
https://github.com/NVIDIA/OpenShell.git
synced 2026-10-02 07:34:45 +08:00
feat(sandbox): add main restart policy (#2798)
* feat(sandbox): add main restart policy Signed-off-by: Drew Newberry <anewberry@nvidia.com> * fix(sandbox): harden policy-driven restarts Signed-off-by: Drew Newberry <anewberry@nvidia.com> * fix(sandbox): address restart review feedback Signed-off-by: Drew Newberry <385+drew@users.noreply.github.com> * fix(sandbox): port restart policy to current runtime Signed-off-by: Drew Newberry <anewberry@nvidia.com> * perf(sandbox): restart promptly after terminal delivery Signed-off-by: Drew Newberry <anewberry@nvidia.com> --------- Signed-off-by: Drew Newberry <anewberry@nvidia.com> Signed-off-by: Drew Newberry <385+drew@users.noreply.github.com>
This commit is contained in:
@@ -18,6 +18,7 @@ use tokio::time::{Instant, sleep};
|
||||
|
||||
const SANDBOX_PRESENCE_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
const SANDBOX_LIST_POLL_INTERVAL: Duration = Duration::from_millis(500);
|
||||
const SANDBOX_RESTART_TIMEOUT: Duration = Duration::from_secs(60);
|
||||
|
||||
fn normalize_output(output: &str) -> String {
|
||||
let stripped = strip_ansi(output).replace('\r', "");
|
||||
@@ -1266,6 +1267,71 @@ async fn canonical_main_exit_255_is_not_retried_as_transport_failure() {
|
||||
|
||||
#[tokio::test]
|
||||
#[serial(sandbox_lifecycle)]
|
||||
async fn on_failure_policy_replaces_runtime_and_preserves_workspace() {
|
||||
const FIRST_MARKER: &str = "initial-main-ready";
|
||||
const SCRIPT: &str = r#"
|
||||
marker=/sandbox/.openshell-restart-e2e
|
||||
if [ -e "$marker" ]; then
|
||||
printf 'replacement-%s\n' "$(cat /proc/sys/kernel/random/uuid)" > /sandbox/replacement-run
|
||||
printf 'replacement-main-ready\n'
|
||||
sleep 300
|
||||
else
|
||||
touch "$marker"
|
||||
printf 'initial-%s\n' "$(cat /proc/sys/kernel/random/uuid)" > /sandbox/initial-run
|
||||
printf 'initial-main-ready\n'
|
||||
sleep 2
|
||||
exit 17
|
||||
fi
|
||||
"#;
|
||||
|
||||
let mut sandbox = SandboxGuard::create_keep_with_args(
|
||||
&["--restart-policy", "on-failure"],
|
||||
&["sh", "-lc", SCRIPT],
|
||||
FIRST_MARKER,
|
||||
)
|
||||
.await
|
||||
.expect("create sandbox with OnFailure restart policy");
|
||||
|
||||
let deadline = Instant::now() + SANDBOX_RESTART_TIMEOUT;
|
||||
let mut observed_replacement_starting = false;
|
||||
let final_details = loop {
|
||||
let details = sandbox_details(&sandbox.name).await;
|
||||
observed_replacement_starting |= details.contains("Phase: Starting");
|
||||
if observed_replacement_starting
|
||||
&& details.contains("Phase: Ready")
|
||||
&& details.contains("Restart count: 1")
|
||||
{
|
||||
break details;
|
||||
}
|
||||
assert!(
|
||||
Instant::now() < deadline,
|
||||
"sandbox did not complete its policy-driven restart within \
|
||||
{SANDBOX_RESTART_TIMEOUT:?}; last details:\n{details}"
|
||||
);
|
||||
sleep(Duration::from_millis(250)).await;
|
||||
};
|
||||
|
||||
assert!(
|
||||
final_details.contains("Restart policy: on-failure"),
|
||||
"restart policy should remain visible after replacement:\n{final_details}"
|
||||
);
|
||||
let runs = sandbox
|
||||
.exec(&["cat", "/sandbox/initial-run", "/sandbox/replacement-run"])
|
||||
.await
|
||||
.expect("replacement sandbox should retain the first run's workspace");
|
||||
assert!(
|
||||
runs.contains("initial-"),
|
||||
"missing initial run marker:\n{runs}"
|
||||
);
|
||||
assert!(
|
||||
runs.contains("replacement-"),
|
||||
"missing replacement run marker:\n{runs}"
|
||||
);
|
||||
|
||||
sandbox.cleanup().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn sandbox_create_with_no_keep_cleans_up_after_tty_command() {
|
||||
let name = format!("tty-{:015x}", rand::random::<u64>() & 0x0fff_ffff_ffff_ffff);
|
||||
// Capture startup diagnostics before --no-keep removes a failed container.
|
||||
|
||||
Reference in New Issue
Block a user