mod support;
use secure_exec_sidecar::wire::{
BootstrapRootFilesystemRequest, ConfigureVmRequest, DisposeReason, DisposeVmRequest,
GuestFilesystemCallRequest, GuestFilesystemOperation, GuestRuntimeKind, MountDescriptor,
MountPluginDescriptor, RequestPayload, ResponsePayload, RootFilesystemEntry,
RootFilesystemEntryEncoding, RootFilesystemEntryKind,
};
use std::collections::HashMap;
use std::fs;
use std::time::Duration;
use support::{
assert_node_available, authenticate_wire, collect_process_output_wire_with_timeout,
create_vm_wire, execute_wire, new_sidecar, open_session_wire, temp_dir, wire_request, wire_vm,
};
#[test]
fn host_mounted_node_modules_package_resolves_from_guest_import() {
assert_node_available();
let mut sidecar = new_sidecar("node-modules-host-mount");
let host_root = temp_dir("node-modules-host-mount-host");
let host_node_modules = host_root.join("node_modules");
let pkg_dir = host_node_modules.join("mypkg");
fs::create_dir_all(&pkg_dir).expect("create host package dir");
fs::write(
pkg_dir.join("package.json"),
r#"{"name":"mypkg","version":"1.0.0","type":"module","main":"index.js"}"#,
)
.expect("write host package.json");
fs::write(
pkg_dir.join("index.js"),
"export default () => \"resolved-from-host-mount\";\n",
)
.expect("write host package entry");
let cwd = temp_dir("node-modules-host-mount-cwd");
let connection_id = authenticate_wire(&mut sidecar, "conn-1");
let session_id = open_session_wire(&mut sidecar, 2, &connection_id);
let (vm_id, _) = create_vm_wire(
&mut sidecar,
3,
&connection_id,
&session_id,
GuestRuntimeKind::JavaScript,
&cwd,
);
sidecar
.dispatch_wire_blocking(wire_request(
4,
wire_vm(&connection_id, &session_id, &vm_id),
RequestPayload::BootstrapRootFilesystemRequest(BootstrapRootFilesystemRequest {
entries: vec![RootFilesystemEntry {
path: String::from("/tmp/node_modules"),
kind: RootFilesystemEntryKind::Directory,
mode: None,
uid: None,
gid: None,
content: None,
encoding: None,
target: None,
executable: false,
}],
}),
))
.expect("bootstrap mountpoint");
sidecar
.dispatch_wire_blocking(wire_request(
5,
wire_vm(&connection_id, &session_id, &vm_id),
RequestPayload::ConfigureVmRequest(ConfigureVmRequest {
mounts: vec![MountDescriptor {
guest_path: String::from("/tmp/node_modules"),
read_only: true,
plugin: MountPluginDescriptor {
id: String::from("host_dir"),
config: serde_json::to_string(&serde_json::json!({
"hostPath": host_node_modules.to_string_lossy(),
"readOnly": true,
}))
.expect("serialize host_dir mount config"),
},
}],
software: Vec::new(),
permissions: None,
module_access_cwd: None,
instructions: Vec::new(),
projected_modules: Vec::new(),
command_permissions: HashMap::new(),
loopback_exempt_ports: Vec::new(),
}),
))
.expect("mount host node_modules");
let entry_source = r#"
import greet from "mypkg";
console.log(greet());
"#;
let write = sidecar
.dispatch_wire_blocking(wire_request(
6,
wire_vm(&connection_id, &session_id, &vm_id),
RequestPayload::GuestFilesystemCallRequest(GuestFilesystemCallRequest {
operation: GuestFilesystemOperation::WriteFile,
path: String::from("/tmp/entry.mjs"),
destination_path: None,
target: None,
content: Some(String::from(entry_source)),
encoding: Some(RootFilesystemEntryEncoding::Utf8),
recursive: false,
mode: None,
uid: None,
gid: None,
atime_ms: None,
mtime_ms: None,
len: None,
offset: None,
}),
))
.expect("write guest entry");
match write.response.payload {
ResponsePayload::GuestFilesystemResultResponse(_) => {}
other => panic!("unexpected guest write response: {other:?}"),
}
execute_wire(
&mut sidecar,
7,
&connection_id,
&session_id,
&vm_id,
"proc-resolve",
GuestRuntimeKind::JavaScript,
std::path::Path::new("/tmp/entry.mjs"),
Vec::new(),
);
let (stdout, stderr, exit) = collect_process_output_wire_with_timeout(
&mut sidecar,
&connection_id,
&session_id,
&vm_id,
"proc-resolve",
Duration::from_secs(10),
);
assert_eq!(
stdout.trim(),
"resolved-from-host-mount",
"guest import of a host-mounted package should resolve; stderr: {stderr}"
);
assert_eq!(exit, 0, "stderr: {stderr}");
sidecar
.dispatch_wire_blocking(wire_request(
8,
wire_vm(&connection_id, &session_id, &vm_id),
RequestPayload::DisposeVmRequest(DisposeVmRequest {
reason: DisposeReason::Requested,
}),
))
.expect("dispose vm");
}