Skip to content
Open
10 changes: 5 additions & 5 deletions computer/arcbox-computer-runtime/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ arcbox-daemon ──vsock──► arcbox-agent ──vsock──► v
```

`arcbox-agent` is this crate's only consumer: it owns the `sandbox.v1`
surface and the vsock transport, and calls `SandboxManager` underneath.
surface and the vsock transport, and calls `ComputerManager` underneath.
There are no service implementations, no tonic, and no daemon here.

The **`vm-agent`** binary that becomes PID 1 *inside* each sandbox is a
Expand All @@ -40,13 +40,13 @@ once, here.

```rust
use std::sync::Arc;
use arcbox_computer_runtime::{NodeEnvironment, RuntimeConfig, SandboxManager, SandboxSpec};
use arcbox_computer_runtime::{ComputerManager, ComputerSpec, NodeEnvironment, RuntimeConfig};

// `environment` is the composer's; see below.
let manager = Arc::new(SandboxManager::new(RuntimeConfig::default(), environment)?);
let manager = Arc::new(ComputerManager::new(RuntimeConfig::default(), environment)?);

let (id, ip) = manager
.create_sandbox(SandboxSpec {
.create_computer(ComputerSpec {
vcpus: 1,
memory_mib: 512,
..Default::default()
Expand All @@ -72,7 +72,7 @@ This crate builds none of them and names no VMM: whoever composes the node
does. For the System VM that is `arcbox_agent::sandbox::node_environment`.

- `driver` — the VMM behind `arcbox_vm_driver::VmDriver`. It must claim
`Prepare` and `Staging` and offer `vsock`, or `SandboxManager::new`
`Prepare` and `Staging` and offer `vsock`, or `ComputerManager::new`
refuses it.
- `network` — what the NICs attach to, behind
`arcbox_vm_driver::net::GuestNetwork`. It must offer `NetworkReconcile`.
Expand Down
22 changes: 11 additions & 11 deletions computer/arcbox-computer-runtime/src/agent/vm_proto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use tokio::sync::mpsc;
use tracing::warn;

use crate::agent::{ExitStatus, OutputChunk};
use crate::error::{Result, VmmError};
use crate::error::{ComputerError, Result};

// Exec-channel vocabulary, shared with vm-agent through `arcbox-vm-proto`.
pub use arcbox_vm_proto::exec::{AGENT_PORT, MSG_WAIT_PORT, READY_PORT, StartCommand, WaitPortReq};
Expand Down Expand Up @@ -114,7 +114,7 @@ pub(crate) async fn connect_to_port(vsock: &dyn Vsock, port: u32) -> Result<Unix
Err(error) => return Err(error.into()),
}
if tokio::time::Instant::now() >= deadline {
return Err(VmmError::Vsock(format!(
return Err(ComputerError::Vsock(format!(
"vsock port {port} did not become ready within {}s",
AGENT_READY_TIMEOUT.as_secs(),
)));
Expand Down Expand Up @@ -156,7 +156,7 @@ fn into_unix_stream(conn: VsockConn) -> Result<UnixStream> {
stream.set_nonblocking(true)?;
Ok(UnixStream::from_std(stream)?)
}
IoMode::Blocking => Err(VmmError::Vsock(
IoMode::Blocking => Err(ComputerError::Vsock(
"vsock connection requires blocking I/O, which the guest-agent client cannot drive"
.into(),
)),
Expand All @@ -170,13 +170,13 @@ pub(crate) async fn wait_ready(listener: &mut dyn VsockListener) -> Result<()> {
let conn = listener
.accept()
.await
.map_err(|e| VmmError::Vsock(format!("accept on ready socket: {e}")))?;
.map_err(|e| ComputerError::Vsock(format!("accept on ready socket: {e}")))?;
let mut stream = into_unix_stream(conn)?;
let mut byte = [0u8; 1];
stream
.read(&mut byte)
.await
.map_err(|e| VmmError::Vsock(format!("read ready byte: {e}")))?;
.map_err(|e| ComputerError::Vsock(format!("read ready byte: {e}")))?;
Ok(())
}

Expand Down Expand Up @@ -249,7 +249,7 @@ async fn drain_output<R: AsyncReadExt + Unpin>(
}
Err(e) => {
let _ = tx
.send(Err(VmmError::Vsock(format!("agent read error: {e}"))))
.send(Err(ComputerError::Vsock(format!("agent read error: {e}"))))
.await;
break;
}
Expand Down Expand Up @@ -355,11 +355,11 @@ mod tests {
// Each final error keeps its native shape through the conversion:
// an I/O failure stays `Io`, a driver `WrongState` stays
// `WrongState` (the guest agent maps that to 412, not 500).
type Native = fn(&VmmError) -> bool;
type Native = fn(&ComputerError) -> bool;
let finals: [(arcbox_vm_driver::Error, Native); 2] = [
(
arcbox_vm_driver::Error::Io(std::io::ErrorKind::BrokenPipe.into()),
|err| matches!(err, VmmError::Io(_)),
|err| matches!(err, ComputerError::Io(_)),
),
(
arcbox_vm_driver::Error::WrongState {
Expand All @@ -369,7 +369,7 @@ mod tests {
),
expected: "running",
},
|err| matches!(err, VmmError::WrongState { expected, actual, .. } if expected == "running" && actual.starts_with("exited")),
|err| matches!(err, ComputerError::WrongState { expected, actual, .. } if expected == "running" && actual.starts_with("exited")),
),
];
for (error, native) in finals {
Expand All @@ -385,7 +385,7 @@ mod tests {
let vsock = ScriptedVsock::new([]);
let err = connect_to_port(&vsock, AGENT_PORT).await.unwrap_err();
assert!(
matches!(err, VmmError::Vsock(ref m) if m.contains("did not become ready within 30s")),
matches!(err, ComputerError::Vsock(ref m) if m.contains("did not become ready within 30s")),
"unexpected error: {err}"
);
assert!(vsock.dials() > 1);
Expand All @@ -396,7 +396,7 @@ mod tests {
let vsock = ScriptedVsock::new([Ok(IoMode::Blocking)]);
let err = connect_to_port(&vsock, AGENT_PORT).await.unwrap_err();
assert!(
matches!(err, VmmError::Vsock(ref m) if m.contains("blocking I/O")),
matches!(err, ComputerError::Vsock(ref m) if m.contains("blocking I/O")),
"unexpected error: {err}"
);
}
Expand Down
18 changes: 9 additions & 9 deletions computer/arcbox-computer-runtime/src/agent/vm_proto/clock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ use tracing::info;

use super::{MSG_CLOCK_SYNC, MSG_EXIT, connect_to_agent, read_frame, write_frame};
use crate::agent::ClockSync;
use crate::error::{Result, VmmError};
use crate::error::{ComputerError, Result};

/// Synchronise the guest clock to the current host time.
///
/// Sends [`MSG_CLOCK_SYNC`] to the exec channel (vsock port 52) and waits for
/// `MSG_EXIT`. Called immediately after `restore_sandbox()` completes so
/// `MSG_EXIT`. Called immediately after `restore_computer()` completes so
/// the guest does not run with a stale timestamp from snapshot creation time,
/// and by the cold-boot path as the agent-readiness gate. `Err` means the
/// round trip itself failed (connect, transport, malformed reply); an agent
Expand All @@ -28,10 +28,10 @@ pub async fn sync_clock(vsock: &dyn Vsock) -> Result<ClockSync> {

let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map_err(|e| VmmError::Vsock(format!("system time error: {e}")))?;
.map_err(|e| ComputerError::Vsock(format!("system time error: {e}")))?;

let secs = i64::try_from(now.as_secs())
.map_err(|e| VmmError::Vsock(format!("unix timestamp overflow: {e}")))?;
.map_err(|e| ComputerError::Vsock(format!("unix timestamp overflow: {e}")))?;
let nanos = now.subsec_nanos();

let result = sync_clock_on_stream(&mut stream, secs, nanos).await;
Expand All @@ -58,20 +58,20 @@ async fn sync_clock_on_stream<S: tokio::io::AsyncReadExt + tokio::io::AsyncWrite

write_frame(stream, MSG_CLOCK_SYNC, &payload)
.await
.map_err(|e| VmmError::Vsock(format!("write MSG_CLOCK_SYNC: {e}")))?;
.map_err(|e| ComputerError::Vsock(format!("write MSG_CLOCK_SYNC: {e}")))?;

let (msg_type, payload) = tokio::time::timeout(Duration::from_secs(5), read_frame(stream))
.await
.map_err(|_| VmmError::Vsock("clock sync: timed out waiting for response".into()))?
.map_err(|e| VmmError::Vsock(format!("read clock sync response: {e}")))?;
.map_err(|_| ComputerError::Vsock("clock sync: timed out waiting for response".into()))?
.map_err(|e| ComputerError::Vsock(format!("read clock sync response: {e}")))?;

if msg_type != MSG_EXIT {
return Err(VmmError::Vsock(format!(
return Err(ComputerError::Vsock(format!(
"clock sync: unexpected response type 0x{msg_type:02x}"
)));
}
if payload.len() < 4 {
return Err(VmmError::Vsock(format!(
return Err(ComputerError::Vsock(format!(
"clock sync: payload too short ({} bytes, expected 4)",
payload.len()
)));
Expand Down
12 changes: 6 additions & 6 deletions computer/arcbox-computer-runtime/src/agent/vm_proto/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use arcbox_vm_proto::exec::{MSG_EOF, MSG_RESIZE, MSG_SIGNAL, MSG_START, MSG_STDI

use super::{StartCommand, connect_to_agent, drain_output, write_frame};
use crate::agent::{ExecInputMsg, OutputChunk};
use crate::error::{Result, VmmError};
use crate::error::{ComputerError, Result};

/// Run a command in the sandbox and stream its output.
///
Expand All @@ -24,15 +24,15 @@ pub async fn run(

// Send the start command.
let payload = serde_json::to_vec(&start)
.map_err(|e| VmmError::Vsock(format!("serialize StartCommand: {e}")))?;
.map_err(|e| ComputerError::Vsock(format!("serialize StartCommand: {e}")))?;
write_frame(&mut stream, MSG_START, &payload)
.await
.map_err(|e| VmmError::Vsock(format!("write MSG_START: {e}")))?;
.map_err(|e| ComputerError::Vsock(format!("write MSG_START: {e}")))?;

// No stdin for run(): close immediately.
write_frame(&mut stream, MSG_EOF, &[])
.await
.map_err(|e| VmmError::Vsock(format!("write MSG_EOF: {e}")))?;
.map_err(|e| ComputerError::Vsock(format!("write MSG_EOF: {e}")))?;

let (tx, rx) = mpsc::channel(64);
tokio::spawn(async move {
Expand All @@ -59,11 +59,11 @@ pub async fn exec(

// Send the start command.
let payload = serde_json::to_vec(&start)
.map_err(|e| VmmError::Vsock(format!("serialize StartCommand: {e}")))?;
.map_err(|e| ComputerError::Vsock(format!("serialize StartCommand: {e}")))?;
let (mut read_half, mut write_half) = tokio::io::split(stream);
write_frame(&mut write_half, MSG_START, &payload)
.await
.map_err(|e| VmmError::Vsock(format!("write MSG_START: {e}")))?;
.map_err(|e| ComputerError::Vsock(format!("write MSG_START: {e}")))?;

let (in_tx, mut in_rx) = mpsc::channel::<ExecInputMsg>(32);
let (out_tx, out_rx) = mpsc::channel::<Result<OutputChunk>>(64);
Expand Down
Loading
Loading