Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 0 additions & 84 deletions e2e/rust/tests/sandbox_lifecycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -779,56 +779,6 @@ async fn canonical_main_nonzero_exit_preserves_status() {
);
}

#[tokio::test]
#[serial(sandbox_lifecycle)]
async fn detached_canonical_main_exit_zero_reaches_completed() {
const RELEASE_PATH: &str = "/sandbox/.openshell-detached-success-release";
let script = format!("while [ ! -e '{RELEASE_PATH}' ]; do sleep 0.05; done; exit 0");
let mut sandbox = SandboxGuard::create_detached_main(&["sh", "-c", &script])
.await
.expect("create detached successful canonical main");
sandbox
.exec(&["touch", RELEASE_PATH])
.await
.expect("release detached successful canonical main");

wait_for_sandbox_phase(&sandbox.name, "Completed", SANDBOX_PRESENCE_TIMEOUT)
.await
.unwrap_or_else(|err| panic!("detached successful main did not complete:\n{err}"));
let details = sandbox_details(&sandbox.name).await;
assert!(
details.contains("Phase: Completed"),
"detached successful main should retain Completed status:\n{details}"
);

sandbox.cleanup().await;
}

#[tokio::test]
#[serial(sandbox_lifecycle)]
async fn detached_canonical_main_nonzero_exit_reaches_error() {
const RELEASE_PATH: &str = "/sandbox/.openshell-detached-failure-release";
let script = format!("while [ ! -e '{RELEASE_PATH}' ]; do sleep 0.05; done; exit 11");
let mut sandbox = SandboxGuard::create_detached_main(&["sh", "-c", &script])
.await
.expect("create detached failing canonical main");
sandbox
.exec(&["touch", RELEASE_PATH])
.await
.expect("release detached failing canonical main");

wait_for_sandbox_phase(&sandbox.name, "Error", SANDBOX_PRESENCE_TIMEOUT)
.await
.unwrap_or_else(|err| panic!("detached failing main did not reach Error:\n{err}"));
let details = sandbox_details(&sandbox.name).await;
assert!(
details.contains("Phase: Error") && details.contains("Exit Code: 11"),
"detached failing main should retain its terminal result:\n{details}"
);

sandbox.cleanup().await;
}

#[tokio::test]
#[serial(sandbox_lifecycle)]
async fn canonical_main_and_exec_receive_declared_environment() {
Expand Down Expand Up @@ -868,40 +818,6 @@ async fn canonical_main_and_exec_receive_declared_environment() {
}
}

#[tokio::test]
#[serial(sandbox_lifecycle)]
async fn detached_main_exit_during_provisioning_is_classified_as_workload_result() {
let mut sandbox = SandboxGuard::create_detached_main(&["sh", "-c", "exit 11"])
.await
.expect("fast detached main exit should not be reported as a provisioning failure");

wait_for_sandbox_phase(&sandbox.name, "Error", SANDBOX_PRESENCE_TIMEOUT)
.await
.unwrap_or_else(|err| panic!("fast detached main did not reach Error:\n{err}"));

let mut get_cmd = openshell_cmd();
get_cmd
.args(["sandbox", "get", &sandbox.name])
.stdout(Stdio::piped())
.stderr(Stdio::piped());
let get_output = get_cmd.output().await.expect("spawn openshell sandbox get");
let details = normalize_output(&format!(
"{}{}",
String::from_utf8_lossy(&get_output.stdout),
String::from_utf8_lossy(&get_output.stderr),
));
assert!(
get_output.status.success(),
"sandbox get failed:\n{details}"
);
assert!(
details.contains("Phase: Error") && details.contains("Exit Code: 11"),
"fast detached main should retain its workload result:\n{details}"
);

sandbox.cleanup().await;
}

#[tokio::test]
#[serial(sandbox_lifecycle)]
async fn canonical_tty_main_uses_sandbox_environment() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

use super::helpers::{expect_terminal_result, run_gated_main};
use openshell_e2e_support::OpenShellRunner;

/// Verify a detached main process that exits nonzero leaves the sandbox in Error
/// with the code recorded.
#[tokio::test]
async fn detached_canonical_main_nonzero_exit_reaches_error() {
let mut runner = OpenShellRunner::from_env("sandbox-lifecycle/detached-exit-nonzero")
.expect("candidate openshell CLI is available");
let result = async {
runner.check_gateway_status().await?;
let runner = &mut runner;
let sandbox_name = format!("ct-{}-dn", runner.id());
run_gated_main(runner, &sandbox_name, 11, "detached-exit-nonzero").await?;
expect_terminal_result(
runner,
&sandbox_name,
"Error",
11,
"detached-exit-nonzero/error",
)
.await?;
Ok(())
}
.await;
if let Err(error) = runner.finish(result).await {
panic!("sandbox-lifecycle/detached-exit-nonzero conformance story failed:\n{error}");
}
}
31 changes: 31 additions & 0 deletions e2e/suites/conformance/cli/tests/lifecycle/detached_exit_zero.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

use super::helpers::{expect_terminal_result, run_gated_main};
use openshell_e2e_support::OpenShellRunner;

/// Verify a detached main process that exits 0 leaves the sandbox in Completed.
#[tokio::test]
async fn detached_canonical_main_exit_zero_reaches_completed() {
let mut runner = OpenShellRunner::from_env("sandbox-lifecycle/detached-exit-zero")
.expect("candidate openshell CLI is available");
let result = async {
runner.check_gateway_status().await?;
let runner = &mut runner;
let sandbox_name = format!("ct-{}-dz", runner.id());
run_gated_main(runner, &sandbox_name, 0, "detached-exit-zero").await?;
expect_terminal_result(
runner,
&sandbox_name,
"Completed",
0,
"detached-exit-zero/completed",
)
.await?;
Ok(())
}
.await;
if let Err(error) = runner.finish(result).await {
panic!("sandbox-lifecycle/detached-exit-zero conformance story failed:\n{error}");
}
}
51 changes: 51 additions & 0 deletions e2e/suites/conformance/cli/tests/lifecycle/detached_fast_exit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

use super::helpers::{create_with_main, expect_terminal_result};
use openshell_e2e_support::OpenShellRunner;

/// Verify a detached main process that exits while the sandbox is still
/// provisioning is reported as a workload result, not a provisioning failure.
#[tokio::test]
async fn detached_main_exit_during_provisioning_is_classified_as_workload_result() {
let mut runner = OpenShellRunner::from_env("sandbox-lifecycle/detached-fast-exit")
.expect("candidate openshell CLI is available");
let result = async {
runner.check_gateway_status().await?;
let runner = &mut runner;
let sandbox_name = format!("ct-{}-fx", runner.id());
create_with_main(
runner,
&sandbox_name,
&["--detach"],
&["sh", "-c", "exit 11"],
"detached-fast-exit/create",
)
.await?
.require_success()?;
let state = expect_terminal_result(
runner,
&sandbox_name,
"Error",
11,
"detached-fast-exit/error",
)
.await?;
// Error alone does not separate a workload result from a provisioning
// failure; the documented Ready reason does.
let ready = state
.conditions
.iter()
.find(|condition| condition.kind == "Ready");
if ready.map(|condition| condition.reason.as_str()) != Some("MainProcessFailed") {
return Err(format!(
"sandbox '{sandbox_name}' Ready condition is {ready:?}; expected reason MainProcessFailed"
));
}
Ok(())
}
.await;
if let Err(error) = runner.finish(result).await {
panic!("sandbox-lifecycle/detached-fast-exit conformance story failed:\n{error}");
}
}
Loading
Loading