mirror of
https://github.com/NVIDIA/OpenShell.git
synced 2026-10-17 05:31:16 +08:00
* feat(vm): boot sandboxes from ext4 root disks * feat(vm): boot sandboxes with writable overlay disk * fix(sandbox): improve sandbox provisioning behavior * fix(vm): repair sandbox image provisioning * fix(vm): clean up rebase fallout * perf(vm): cache empty overlay images * perf(vm): skip repeated sandbox ownership repair * fix(vm): address lifecycle review issues * fix(vm): repair gateway vm startup * wip
140 lines
4.6 KiB
Rust
140 lines
4.6 KiB
Rust
// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
#![cfg(feature = "e2e")]
|
|
|
|
//! Smoke test: verify the gateway is healthy, create a sandbox, exec a
|
|
//! command inside it, and tear it down.
|
|
//!
|
|
//! This test is cluster-agnostic — it works against any running gateway
|
|
//! (Docker-based cluster or openshell-driver-vm microVM). The `e2e:vm` mise
|
|
//! task uses it to validate the VM gateway after boot.
|
|
|
|
use std::process::Stdio;
|
|
use std::time::Duration;
|
|
|
|
use openshell_e2e::harness::binary::openshell_cmd;
|
|
use openshell_e2e::harness::output::strip_ansi;
|
|
use openshell_e2e::harness::sandbox::SandboxGuard;
|
|
|
|
/// End-to-end smoke test: status → create → exec → list → delete.
|
|
#[tokio::test]
|
|
async fn gateway_smoke() {
|
|
// ── 1. Gateway must be reachable ──────────────────────────────────
|
|
let mut clean_status = String::new();
|
|
let mut status_ok = false;
|
|
for _ in 0..15 {
|
|
let mut status_cmd = openshell_cmd();
|
|
status_cmd
|
|
.arg("status")
|
|
.stdout(Stdio::piped())
|
|
.stderr(Stdio::piped());
|
|
|
|
let status_out = status_cmd
|
|
.output()
|
|
.await
|
|
.expect("failed to run openshell status");
|
|
|
|
let status_text = format!(
|
|
"{}{}",
|
|
String::from_utf8_lossy(&status_out.stdout),
|
|
String::from_utf8_lossy(&status_out.stderr),
|
|
);
|
|
clean_status = strip_ansi(&status_text);
|
|
|
|
if status_out.status.success() && clean_status.contains("Connected") {
|
|
status_ok = true;
|
|
break;
|
|
}
|
|
|
|
tokio::time::sleep(Duration::from_secs(2)).await;
|
|
}
|
|
|
|
assert!(
|
|
status_ok,
|
|
"openshell status never became healthy:\n{clean_status}",
|
|
);
|
|
|
|
// ── 2. Create a sandbox and exec a command ───────────────────────
|
|
// Default behaviour keeps the sandbox alive after the command exits,
|
|
// so we can verify it in the list before cleaning up.
|
|
let mut sb = SandboxGuard::create(&["--", "echo", "smoke-ok"])
|
|
.await
|
|
.expect("sandbox create should succeed");
|
|
|
|
assert!(
|
|
sb.create_output.contains("smoke-ok"),
|
|
"expected 'smoke-ok' in sandbox output:\n{}",
|
|
sb.create_output,
|
|
);
|
|
|
|
if std::env::var_os("OPENSHELL_E2E_EXPECT_VM_OVERLAY").is_some() {
|
|
assert_vm_overlay_root(&sb.name).await;
|
|
}
|
|
|
|
// ── 3. Verify the sandbox appeared in the list ───────────────────
|
|
let mut list_cmd = openshell_cmd();
|
|
list_cmd
|
|
.args(["sandbox", "list", "--names"])
|
|
.stdout(Stdio::piped())
|
|
.stderr(Stdio::piped());
|
|
|
|
let list_out = list_cmd
|
|
.output()
|
|
.await
|
|
.expect("failed to run openshell sandbox list");
|
|
|
|
let list_text = strip_ansi(&format!(
|
|
"{}{}",
|
|
String::from_utf8_lossy(&list_out.stdout),
|
|
String::from_utf8_lossy(&list_out.stderr),
|
|
));
|
|
|
|
assert!(
|
|
list_text.contains(&sb.name),
|
|
"sandbox '{}' should appear in list output:\n{list_text}",
|
|
sb.name,
|
|
);
|
|
|
|
// ── 4. Cleanup ───────────────────────────────────────────────────
|
|
sb.cleanup().await;
|
|
}
|
|
|
|
async fn assert_vm_overlay_root(sandbox_name: &str) {
|
|
let script = concat!(
|
|
"set -eu; ",
|
|
"test \"$(stat -f -c %T /)\" = \"overlayfs\"; ",
|
|
"printf \"overlay-write\\n\" > /sandbox/overlay-check; ",
|
|
"test \"$(cat /sandbox/overlay-check)\" = \"overlay-write\"; ",
|
|
"if [ -e /opt/openshell/tls/tls.key ]; then ",
|
|
"test \"$(stat -c %a /opt/openshell/tls/tls.key)\" = \"600\"; ",
|
|
"fi; ",
|
|
"echo vm-overlay-ok",
|
|
);
|
|
|
|
let mut exec_cmd = openshell_cmd();
|
|
exec_cmd
|
|
.args(["sandbox", "exec", "--name", sandbox_name, "--no-tty", "--"])
|
|
.arg("sh")
|
|
.arg("-lc")
|
|
.arg(script)
|
|
.stdout(Stdio::piped())
|
|
.stderr(Stdio::piped());
|
|
|
|
let output = exec_cmd
|
|
.output()
|
|
.await
|
|
.expect("failed to run VM overlay assertion");
|
|
let combined = strip_ansi(&format!(
|
|
"{}{}",
|
|
String::from_utf8_lossy(&output.stdout),
|
|
String::from_utf8_lossy(&output.stderr),
|
|
));
|
|
|
|
assert!(
|
|
output.status.success() && combined.contains("vm-overlay-ok"),
|
|
"VM overlay assertion failed (status {:?}):\n{combined}",
|
|
output.status.code(),
|
|
);
|
|
}
|