Skip to content
Merged
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
7 changes: 7 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ jobs:
components: rustfmt, clippy
targets: wasm32-wasip2, x86_64-pc-windows-gnu
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
# Ubuntu's AppArmor lets an unprivileged process create a user namespace but not map
# its uid in it, so no confined test could run. A laptop installs the amem profile in
# packaging/apparmor; the tests' own probe is unshare, not amem, so this throwaway VM
# turns the restriction off instead
- run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
- run: make check
# the embedded PostgreSQL for the ledger tests lists its releases through GitHub's
# API; unauthenticated, a runner's address is soon refused
Expand Down Expand Up @@ -164,6 +169,8 @@ jobs:
- uses: taiki-e/install-action@83ac0ad63c0167e6f06796fab0fce28db1bf3db0 # v2.87.22
with:
tool: cargo-llvm-cov
# the confined tests run here too, as in the check job
- run: sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
- run: make coverage
# the embedded PostgreSQL for the ledger tests lists its releases through GitHub's
# API; unauthenticated, a runner's address is soon refused
Expand Down
20 changes: 15 additions & 5 deletions crates/memory/tests/launch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,16 +205,21 @@ fn launch_refuses_without_the_credential_its_mode_needs() {
}

/// Whether this kernel lets an unprivileged process create user and network namespaces.
/// Ubuntu's AppArmor restriction and some CI kernels say no; the confined test is then
/// reported as skipped rather than failed.
/// Ubuntu's AppArmor restriction says no by default; a confined test is then reported as
/// skipped rather than failed, except on CI, which is set up to run it and fails instead.
#[cfg(target_os = "linux")]
fn namespaces_available() -> bool {
Command::new("unshare")
let available = Command::new("unshare")
.args(["--user", "--map-root-user", "--net", "--mount", "true"])
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()
.is_ok_and(|s| s.success())
.is_ok_and(|s| s.success());
assert!(
available || std::env::var_os("CI").is_none(),
"unprivileged namespaces are not available, and on CI the confined tests must run"
);
available
}

#[cfg(target_os = "linux")]
Expand Down Expand Up @@ -1037,7 +1042,8 @@ fn a_kept_tree_is_the_commit_and_repeated_runs_are_not_a_subscription_s() {

/// The projection as a live mount: a shell `cat` inside the sandbox, which no hook
/// sees, is a `get` the memory serves and logs. Where this host has no FUSE the launch
/// says the projection is a render, and the test says it skipped the rest.
/// says the projection is a render, and the test says it skipped the rest; on CI, which
/// has FUSE, that is a failure.
#[cfg(target_os = "linux")]
#[test]
fn a_file_read_inside_the_sandbox_is_a_get_the_memory_serves() {
Expand Down Expand Up @@ -1079,6 +1085,10 @@ fn a_file_read_inside_the_sandbox_is_a_get_the_memory_serves() {
let reads = dm_server::reads::all(&root).unwrap();
let gets: Vec<_> = reads.iter().filter(|r| r.tool == "get").collect();
if stderr.contains("is a render:") {
assert!(
std::env::var_os("CI").is_none(),
"on CI the projection must be a live mount: {stderr}"
);
eprintln!("skipping the rest: {stderr}");
assert!(gets.is_empty(), "no hook, no mount, no get: {gets:?}");
return;
Expand Down
22 changes: 17 additions & 5 deletions crates/mount/tests/live.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! The mount, live: a memory's render served over FUSE, read with ordinary file calls.
//! Skipped where the host has no FUSE, saying so.
//! Skipped where the host has no FUSE, saying so; on CI that is a failure.

// An integration test is test code; a panic here is the failure being reported.
#![allow(clippy::unwrap_used)]
Expand Down Expand Up @@ -30,6 +30,20 @@ async fn rendered(root: &Path) -> (Arc<Memory>, String, PathBuf) {
(m, w.id, render)
}

/// Whether this host can serve a FUSE mount. Without it a test says it skipped, except on
/// CI, which is set up to run it and fails instead.
fn fuse_available() -> bool {
let Err(why) = dm_mount::availability() else {
return true;
};
assert!(
std::env::var_os("CI").is_none(),
"{why}, and on CI the mount tests must run"
);
eprintln!("skipping: {why}");
false
}

fn names(dir: &Path) -> Vec<String> {
let mut v: Vec<String> = std::fs::read_dir(dir)
.unwrap()
Expand All @@ -42,8 +56,7 @@ fn names(dir: &Path) -> Vec<String> {

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn an_open_in_the_mount_is_a_get_the_memory_serves_logs_and_tapes() {
if let Err(why) = dm_mount::availability() {
eprintln!("skipping: {why}");
if !fuse_available() {
return;
}
let dir = tempfile::tempdir().unwrap();
Expand Down Expand Up @@ -124,8 +137,7 @@ async fn an_open_in_the_mount_is_a_get_the_memory_serves_logs_and_tapes() {

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn the_record_s_word_decides_an_open_not_the_render_s() {
if let Err(why) = dm_mount::availability() {
eprintln!("skipping: {why}");
if !fuse_available() {
return;
}
let dir = tempfile::tempdir().unwrap();
Expand Down
Loading