use std::{ io::{Read, Write}, process::{Command, Stdio}, thread, time::{Duration, Instant, SystemTime, UNIX_EPOCH}, }; use testcontainers::core::error::{ClientError, WaitContainerError}; use testcontainers::{GenericImage, ImageExt, TestcontainersError, runners::SyncRunner}; use super::{ container_process::{ docker_text, docker_text_with_timeout, docker_text_with_timeout_and_limits, }, container_selection::Selection, }; const IMAGE: &str = "wireguard-admin-alpine:3.22.7"; const IMAGE_ID: &str = "sha256:f41cd6b848d8280d4d86bcaceea962e1b3e5a73e9496c6f9d7929470d0e874d5"; const NETWORK_IPV4: &str = "192.168.253.0/24"; const CONTAINER_STARTUP_TIMEOUT: Duration = Duration::from_secs(12 * 60); const FIXTURE_COMMAND_TIMEOUT_SECONDS: &str = "15"; const FIXTURE_TRANSPORT_TIMEOUT: Duration = Duration::from_secs(20); const DRIVER_TRANSPORT_TIMEOUT: Duration = Duration::from_secs(180); const COMMAND_STDOUT_LIMIT: usize = 1024; const NAMESPACE_STDOUT_LIMIT: usize = 128; const COMMAND_STDERR_LIMIT: usize = 1024; const MAX_PHASE_REQUEST_BYTES: usize = 4096; #[derive(Clone, Debug)] pub struct ProbeNames { pub network: String, pub server: String, pub baseline: String, pub client: String, } impl ProbeNames { pub fn unique() -> Self { let nonce = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap_or_default() .as_nanos(); let suffix = format!("{}-{nonce:x}", std::process::id()); Self { network: format!("wga-m83-net-{suffix}"), server: format!("wga-m83-server-{suffix}"), baseline: format!("wga-m85-baseline-{suffix}"), client: format!("wga-m83-client-{suffix}"), } } } pub fn run_probe(names: &ProbeNames) -> Result<(), &'static str> { let selection = Selection::from_process_environment()?; verify_daemon(&selection)?; verify_cached_image(&selection)?; let network = OwnedNetwork::create(&selection, names.network.clone())?; let server = start_fixture_container(&selection, names.server.as_str(), &network.name)?; let baseline = start_fixture_container(&selection, names.baseline.as_str(), &network.name)?; let client = start_fixture_container(&selection, names.client.as_str(), &network.name)?; verify_distinct_namespaces(&server, &baseline)?; verify_distinct_namespaces(&server, &client)?; verify_distinct_namespaces(&baseline, &client)?; verify_wireguard_and_ipv4(&server)?; verify_wireguard_and_ipv4(&client)?; run_application_smoke(&server, &baseline, &client)?; client.remove()?; baseline.remove()?; server.remove()?; network.remove() } fn run_application_smoke( server: &OwnedContainer, baseline_client: &OwnedContainer, client: &OwnedContainer, ) -> Result<(), &'static str> { exec_smoke_command( server, &["ip", "link", "set", "dev", "wg-m83-probe", "up"], "Fixture WireGuard link setup failed", )?; exec_smoke_command( server, &[ "ip", "-4", "address", "add", "10.20.0.1/24", "dev", "wg-m83-probe", ], "Fixture IPv4 address setup failed", )?; let driver = std::env::var_os("WIREGUARD_ADMIN_TEST_DRIVER") .ok_or("musl test driver path was not explicitly selected")?; let driver = std::path::PathBuf::from(driver); let metadata = std::fs::symlink_metadata(&driver) .map_err(|_| "musl test driver could not be inspected")?; if metadata.file_type().is_symlink() || !metadata.is_file() || metadata.len() > 256 * 1024 * 1024 { return Err("musl test driver is not a bounded regular file"); } let endpoint = format!("--host={}", server.selection.endpoint); let remote_driver = "/tmp/wga-m8-driver"; docker_text_with_timeout( &[ &endpoint, "cp", driver .to_str() .ok_or("musl test driver path is not UTF-8")?, &format!("{}:{remote_driver}", server.container().id()), ], Duration::from_secs(180), ) .map_err(|category| match category { "Docker command timed out" => "Application fixture driver staging timed out", _ => "Application fixture driver staging failed", })?; exec_smoke_command( server, &["chmod", "0700", remote_driver], "Application fixture driver permission setup failed", )?; let command = vec![ endpoint.as_str(), "exec", "-i", "--env=WIREGUARD_ADMIN_FIXTURE_ROOT=/tmp/wireguard-admin-fixture", server.container().id(), "/bin/busybox", "timeout", "-s", "KILL", "150", remote_driver, "--ignored", "--exact", "container_driver::alpine_scenario_driver", "--test-threads=1", "--nocapture", ]; let empty = invoke_driver(&command, b"OBSERVE\nEND\n")?; if !empty.contains("phase=observe status=complete interface=wg-m83-probe peers=0") { return Err("real runtime reader did not report the initial empty peer set"); } let baseline = invoke_driver(&command, b"BASELINE\nEND\n")?; let baseline_key = phase_public_key(&baseline, "baseline_apply")?; let unconnected = invoke_driver(&command, b"OBSERVE\nEND\n")?; let unconnected_peer = unconnected .lines() .find(|line| line.starts_with(&format!("peer={baseline_key} "))) .ok_or("real runtime reader omitted the unconnected baseline peer")?; if !peer_has_exact_ipv4_routes(unconnected_peer, "10.20.0.10/32") || !unconnected_peer.contains("handshake=none") || !unconnected_peer.contains("received=0") || !unconnected_peer.contains("transmitted=0") { return Err("real runtime reader did not report the baseline pre-handshake state"); } let baseline_temp = tempfile::Builder::new() .prefix("wga-m85-baseline-") .tempdir() .map_err(|_| "private baseline staging directory could not be created")?; use std::os::unix::fs::PermissionsExt; std::fs::set_permissions(baseline_temp.path(), std::fs::Permissions::from_mode(0o700)) .map_err(|_| "private baseline staging directory permissions failed")?; let baseline_file = baseline_temp.path().join("baseline-client.conf"); docker_text_with_timeout( &[ &endpoint, "cp", &format!( "{}:/tmp/wireguard-admin-fixture/baseline-client.conf", server.container().id() ), baseline_file .to_str() .ok_or("private baseline path was not UTF-8")?, ], Duration::from_secs(20), ) .map_err(|_| "synthetic baseline client transfer failed")?; let baseline_metadata = std::fs::symlink_metadata(&baseline_file) .map_err(|_| "synthetic baseline client file could not be inspected")?; use std::os::unix::fs::MetadataExt; if baseline_metadata.file_type().is_symlink() || !baseline_metadata.is_file() || baseline_metadata.len() > 64 * 1024 || baseline_metadata.mode() & 0o777 != 0o600 { return Err("synthetic baseline client was not a bounded mode-0600 regular file"); } let baseline_bytes = std::fs::read(&baseline_file) .map_err(|_| "synthetic baseline client file could not be read")?; let (baseline_address, baseline_endpoint) = client_connection_material(&baseline_bytes)?; const REMOTE_BASELINE_DELIVERY: &str = "/tmp/wgb85.conf"; const REMOTE_BASELINE_SETCONF: &str = "/tmp/wgb85-set.conf"; docker_text_with_timeout( &[ &endpoint, "cp", baseline_file .to_str() .ok_or("private baseline path was not UTF-8")?, &format!( "{}:{REMOTE_BASELINE_DELIVERY}", baseline_client.container().id() ), ], Duration::from_secs(20), ) .map_err(|_| "synthetic baseline client transfer failed")?; verify_private_container_file(baseline_client, REMOTE_BASELINE_DELIVERY)?; strip_client_config( baseline_client, REMOTE_BASELINE_DELIVERY, REMOTE_BASELINE_SETCONF, )?; verify_private_container_file(baseline_client, REMOTE_BASELINE_SETCONF)?; setup_client_interface( baseline_client, "wg-m85-baseline", REMOTE_BASELINE_SETCONF, &baseline_address, &baseline_endpoint, )?; exec_success( baseline_client, ["ping", "-4", "-c", "3", "-W", "2", "10.20.0.1"], ) .map_err(|_| "synthetic baseline IPv4 tunnel traffic failed")?; let mut baseline_observed = None; for _ in 0..10 { let observation = invoke_driver(&command, b"OBSERVE\nEND\n")?; let peer = observation .lines() .find(|line| line.starts_with(&format!("peer={baseline_key} "))); if peer.is_some_and(|line| { peer_has_exact_ipv4_routes(line, "10.20.0.10/32") && line.contains("handshake=present") && counter_is_positive(line, "received=") && counter_is_positive(line, "transmitted=") }) { baseline_observed = Some(observation); break; } thread::sleep(Duration::from_millis(250)); } if baseline_observed.is_none() { return Err("real runtime reader did not observe the baseline peer and traffic"); } // Create, Commit and first delivery share this one Application session. // Only fixed phase requests cross stdin; key/config bytes remain in private // fixture files and are transferred separately by docker cp. let report = docker_exchange_report( &command, b"CREATE\tM8 smoke client\nCOMMIT\nDELIVER\nEND\n", DRIVER_TRANSPORT_TIMEOUT, 2048, 1024, ) .map_err(|category| match category { "Docker command timed out" => { "Application fixture session timed out; in-container completion is uncertain" } "In-container fixture command timed out" => { "Application fixture driver exceeded its in-container deadline" } "Docker command output exceeded its limit" => { "Application fixture driver output exceeded its limit" } _ => "Application fixture driver protocol failed", })?; let phase_report = format!("{}\n{}", report.stdout, report.stderr); if !driver_ran_exactly_one_test(&phase_report) { return Err("Application fixture driver selection did not execute exactly one test"); } if report.failure_category.is_some() { return Err(driver_phase_failure_category(&phase_report)); } for marker in [ "phase=fixture_bootstrap status=ok", "phase=create status=ok public_key=", "phase=commit status=ok", "phase=delivery status=ok activation_warning=true", "phase=end status=ok", ] { if !phase_report.contains(marker) { return Err("Application fixture report omitted a required creation phase"); } } let public_key = phase_public_key(&phase_report, "create")?; let baseline_archive = phase_archive_name(&phase_report, "commit")?; let temp = tempfile::Builder::new() .prefix("wga-m85-delivery-") .tempdir() .map_err(|_| "private delivery staging directory could not be created")?; std::fs::set_permissions(temp.path(), std::fs::Permissions::from_mode(0o700)) .map_err(|_| "private delivery staging directory permissions failed")?; let delivered = temp.path().join("delivered.conf"); let endpoint = format!("--host={}", server.selection.endpoint); docker_text_with_timeout( &[ &endpoint, "cp", &format!( "{}:/tmp/wireguard-admin-fixture/first-delivery.conf", server.container().id() ), delivered .to_str() .ok_or("private delivery path was not UTF-8")?, ], Duration::from_secs(20), ) .map_err(|_| "first delivery transfer from server fixture failed")?; let metadata = std::fs::symlink_metadata(&delivered) .map_err(|_| "first delivery file could not be inspected")?; if metadata.file_type().is_symlink() || !metadata.is_file() || metadata.len() > 64 * 1024 || metadata.mode() & 0o777 != 0o600 { return Err("first delivery file was not a bounded mode-0600 regular file"); } let bytes = std::fs::read(&delivered).map_err(|_| "first delivery file could not be read")?; let (address, endpoint_addr) = client_connection_material(&bytes)?; // wg-quick strip derives an interface name from the config basename and // rejects names longer than Linux's 15-character interface limit. let remote_delivery = "/tmp/wgm85.conf"; let remote_setconf = "/tmp/wgm85-set.conf"; docker_text_with_timeout( &[ &endpoint, "cp", delivered .to_str() .ok_or("private client path was not UTF-8")?, &format!("{}:{remote_delivery}", client.container().id()), ], Duration::from_secs(20), ) .map_err(|_| "first delivery transfer to client fixture failed")?; verify_private_container_file(client, remote_delivery)?; verify_container_copy_matches( client, remote_delivery, &temp.path().join("delivered-copy.conf"), &bytes, &endpoint, )?; strip_client_config(client, remote_delivery, remote_setconf)?; verify_private_container_file(client, remote_setconf)?; setup_client_interface( client, "wg-m85-client", remote_setconf, &address, &endpoint_addr, )?; let before = invoke_driver(&command, b"OBSERVE\nEND\n")?; if !before.contains("phase=observe status=complete interface=wg-m83-probe peers=1") || !before.lines().any(|line| { line.starts_with(&format!("peer={baseline_key} ")) && peer_has_exact_ipv4_routes(line, "10.20.0.10/32") }) || before.contains(&format!("peer={public_key} ")) { return Err("real runtime reader did not report only the baseline peer before Apply"); } for _ in 0..3 { expect_fixture_ping_failure(client, "10.20.0.1")?; } let apply = invoke_driver( &command, b"APPLY\t/tmp/wireguard-admin-fixture/server.conf\nEND\n", )?; if !apply.contains("phase=fixture_apply status=verified") { return Err("fixture Apply did not pass real runtime peer verification"); } let post_apply = invoke_driver(&command, b"OBSERVE\nEND\n")?; let baseline_post_apply = post_apply .lines() .find(|line| line.starts_with(&format!("peer={baseline_key} "))) .filter(|line| peer_has_exact_ipv4_routes(line, "10.20.0.10/32")) .and_then(peer_counters) .ok_or("real runtime reader omitted the baseline peer after new-client Apply")?; // Reapply the private client peer configuration after the fixture server // setconf. This explicitly reestablishes a handshake from the baseline // client instead of trusting counters from the pre-Apply session. eprintln!("phase=baseline_reconnect status=started reason=fixture_server_setconf"); exec_success( baseline_client, ["wg", "setconf", "wg-m85-baseline", REMOTE_BASELINE_SETCONF], ) .map_err(|_| "baseline client fixture reconnect failed after new-client Apply")?; eprintln!("phase=baseline_reconnect status=complete"); let (transmitted, received) = bounded_ping_summary(baseline_client, "10.20.0.1")?; if received == 0 { report_baseline_failure( server, &command, baseline_key, "after baseline fixture reconnect", )?; eprintln!( "baseline ping after fixture reconnect: transmitted={transmitted} received={received}" ); return Err( "pre-existing baseline client stopped after new-client Apply and fixture reconnect", ); } let mut baseline_after_apply = None; for _ in 0..10 { let report = invoke_driver(&command, b"OBSERVE\nEND\n")?; if let Some(line) = report .lines() .find(|line| line.starts_with(&format!("peer={baseline_key} "))) && peer_has_exact_ipv4_routes(line, "10.20.0.10/32") && line.contains("handshake=present") && peer_counters(line).is_some_and(|after| { after.0 > baseline_post_apply.0 && after.1 > baseline_post_apply.1 }) { baseline_after_apply = Some(report); break; } thread::sleep(Duration::from_millis(250)); } if baseline_after_apply.is_none() { return Err( "real runtime reader did not observe fresh baseline traffic after new-client Apply", ); } let before_traffic = invoke_driver(&command, b"OBSERVE\nEND\n")?; let pre_traffic_peer = before_traffic .lines() .find(|line| line.starts_with(&format!("peer={public_key} "))) .ok_or("real runtime reader omitted the applied peer before traffic")?; let pre_traffic_counters = peer_counters(pre_traffic_peer) .ok_or("real runtime reader counters were unavailable before traffic")?; exec_success(client, ["ping", "-4", "-c", "3", "-W", "2", "10.20.0.1"]) .map_err(|_| "client IPv4 tunnel traffic failed after fixture Apply")?; let mut observed = None; for _ in 0..10 { let report = invoke_driver(&command, b"OBSERVE\nEND\n")?; if let Some(line) = report .lines() .find(|line| line.starts_with(&format!("peer={public_key} "))) && peer_has_exact_ipv4_routes(line, &address) && line.contains("handshake=present") && peer_counters(line).is_some_and(|after| { after.0 > pre_traffic_counters.0 && after.1 > pre_traffic_counters.1 }) { observed = Some(report); break; } thread::sleep(Duration::from_millis(250)); } if observed.is_none() { let last = invoke_driver(&command, b"OBSERVE\nEND\n")?; if let Some(peer) = last .lines() .find(|line| line.starts_with(&format!("peer={public_key} "))) { eprintln!("Rustix reader final new-peer sample: {peer}"); } } let observed = observed.ok_or("real runtime reader did not observe the client handshake")?; let peer = observed .lines() .find(|line| line.starts_with(&format!("peer={public_key} "))) .ok_or("real runtime reader omitted the created client")?; if !peer_has_exact_ipv4_routes(peer, &address) || !peer.contains("handshake=present") || !counter_is_positive(peer, "received=") || !counter_is_positive(peer, "transmitted=") || !endpoint_addr.contains(":51820") { return Err("real runtime reader reported incomplete IPv4 tunnel state"); } // Commit the deletion through the real Application while the fixture's // running interface still has the peer. The request carries only the // public identity and address; key/config material stays in private files. let pre_delete_counters = peer_counters(peer) .ok_or("real runtime reader counters were unavailable before deletion")?; let delete_request = format!("DELETE\t{public_key}\t{address}\nEND\n"); let deletion = invoke_driver(&command, delete_request.as_bytes())?; if !deletion.contains(&format!( "phase=delete status=saved public_key={public_key} current_record=removed backup_record=retained address_reserved=true" )) { return Err("Application deletion did not preserve the backup and address reservation"); } let restore_archive = phase_archive_name(&deletion, "delete")?; let saved_but_live = invoke_driver(&command, b"OBSERVE\nEND\n")?; if !saved_but_live.contains("phase=observe status=complete interface=wg-m83-probe peers=2") || !saved_but_live.lines().any(|line| { line.starts_with(&format!("peer={public_key} ")) && peer_has_exact_ipv4_routes(line, &address) }) || !saved_but_live.lines().any(|line| { line.starts_with(&format!("peer={baseline_key} ")) && peer_has_exact_ipv4_routes(line, "10.20.0.10/32") }) { return Err("saved deletion changed the live peer set before explicit Apply"); } exec_success(client, ["ping", "-4", "-c", "2", "-W", "2", "10.20.0.1"]) .map_err(|_| "deleted client's live IPv4 access stopped before explicit Apply")?; exec_success( baseline_client, ["ping", "-4", "-c", "3", "-W", "2", "10.20.0.1"], ) .map_err(|_| "pre-existing baseline client stopped before explicit Apply")?; let mut continued_access = None; for _ in 0..10 { let report = invoke_driver(&command, b"OBSERVE\nEND\n")?; let live_peer = report .lines() .find(|line| line.starts_with(&format!("peer={public_key} "))); let baseline_peer = report .lines() .find(|line| line.starts_with(&format!("peer={baseline_key} "))); if live_peer.is_some_and(|line| { peer_counters(line).is_some_and(|after| { after.0 > pre_delete_counters.0 && after.1 > pre_delete_counters.1 }) }) && baseline_peer.is_some_and(|line| { peer_has_exact_ipv4_routes(line, "10.20.0.10/32") && line.contains("handshake=present") && counter_is_positive(line, "received=") && counter_is_positive(line, "transmitted=") }) { continued_access = Some(report); break; } thread::sleep(Duration::from_millis(250)); } if continued_access.is_none() { return Err("fresh tunnel traffic did not prove access persisted after saved deletion"); } let apply_request = format!("APPLY\t/tmp/wireguard-admin-fixture/server.conf\t{address}\nEND\n"); let apply = invoke_driver(&command, apply_request.as_bytes())?; if !apply.contains("phase=fixture_apply status=verified address_released=true") { return Err("explicit fixture Apply did not verify deletion or release the address"); } let after_apply = invoke_driver(&command, b"OBSERVE\nEND\n")?; let baseline_after_apply = after_apply .lines() .find(|line| line.starts_with(&format!("peer={baseline_key} "))) .filter(|line| peer_has_exact_ipv4_routes(line, "10.20.0.10/32")); if !after_apply.contains("phase=observe status=complete interface=wg-m83-probe peers=1") || after_apply.contains(&format!("peer={public_key} ")) || baseline_after_apply.is_none() { return Err( "real runtime reader did not verify deleted peer removal and baseline retention", ); } let baseline_after_apply = baseline_after_apply .and_then(peer_counters) .ok_or("baseline counters were unavailable after deletion Apply")?; // These are new attempts after the successful runtime verification. A // separate baseline reconnect and successful probe guard against setup // failure after the full server peer-set replacement. for _ in 0..3 { expect_fixture_ping_failure(client, "10.20.0.1")?; } eprintln!("phase=baseline_reconnect status=started reason=deletion_apply"); exec_success( baseline_client, ["wg", "setconf", "wg-m85-baseline", REMOTE_BASELINE_SETCONF], ) .map_err(|_| "baseline client fixture reconnect failed after deletion Apply")?; eprintln!("phase=baseline_reconnect status=complete reason=deletion_apply"); let (transmitted, received) = bounded_ping_summary(baseline_client, "10.20.0.1")?; if received == 0 { report_baseline_failure( server, &command, baseline_key, "after deletion Apply baseline reconnect", )?; eprintln!( "baseline ping after deletion Apply: transmitted={transmitted} received={received}" ); return Err( "pre-existing baseline client stopped after explicit Apply and fixture reconnect", ); } let mut baseline_verified = false; for _ in 0..10 { let report = invoke_driver(&command, b"OBSERVE\nEND\n")?; if report.contains("phase=observe status=complete interface=wg-m83-probe peers=1") && !report.contains(&format!("peer={public_key} ")) && report.lines().any(|line| { line.starts_with(&format!("peer={baseline_key} ")) && peer_has_exact_ipv4_routes(line, "10.20.0.10/32") && line.contains("handshake=present") && peer_counters(line).is_some_and(|after| { after.0 > baseline_after_apply.0 && after.1 > baseline_after_apply.1 }) }) { baseline_verified = true; break; } thread::sleep(Duration::from_millis(250)); } if !baseline_verified { return Err( "real runtime reader did not verify fresh baseline traffic after deletion Apply", ); } // Restore the exact pre-delete archive emitted by this Application Commit. // It contains the complete prior client set and vault while runtime is // currently baseline-only. Restore must replace saved files only. let restore_request = format!("RESTORE\t{restore_archive}\t{address}\nEND\n"); let restore = invoke_driver(&command, restore_request.as_bytes())?; if !restore.contains(&format!( "phase=restore status=confirmed archive={restore_archive} reason=pre-commit precautionary_backup=pre-restore settings=true server=true vault=true target_client_present=true pending_address_reserved=false runtime_unchanged=true" )) { return Err("password-free restore did not verify the exact archive and precautionary backup"); } let saved_restored_live = invoke_driver(&command, b"OBSERVE\nEND\n")?; if !saved_restored_live.contains("phase=observe status=complete interface=wg-m83-probe peers=1") || saved_restored_live.contains(&format!("peer={public_key} ")) || !saved_restored_live.lines().any(|line| { line.starts_with(&format!("peer={baseline_key} ")) && peer_has_exact_ipv4_routes(line, "10.20.0.10/32") }) { return Err("restore changed the live peer set before explicit fixture Apply"); } for _ in 0..3 { expect_fixture_ping_failure(client, "10.20.0.1")?; } let restore_apply = invoke_driver( &command, format!("APPLY\t/tmp/wireguard-admin-fixture/server.conf\t{address}\nEND\n").as_bytes(), )?; if !restore_apply.contains("phase=fixture_apply status=verified address_released=true") { return Err( "explicit fixture Apply after restore did not verify the restored saved peer set", ); } let before_restored_traffic = invoke_driver(&command, b"OBSERVE\nEND\n")?; let restored_before = before_restored_traffic .lines() .find(|line| line.starts_with(&format!("peer={public_key} "))) .filter(|line| peer_has_exact_ipv4_routes(line, &address)) .and_then(peer_counters) .ok_or("real runtime reader omitted the restored peer after explicit Apply")?; eprintln!("phase=client_reconnect status=started reason=restore_apply"); exec_success(client, ["wg", "setconf", "wg-m85-client", remote_setconf]) .map_err(|_| "restored client fixture reconnect failed after restore Apply")?; eprintln!("phase=client_reconnect status=complete reason=restore_apply"); let (transmitted, received) = bounded_ping_summary(client, "10.20.0.1")?; if received == 0 { return Err("restored client did not renew IPv4 tunnel traffic after explicit Apply"); } let mut restored_traffic = false; for _ in 0..10 { let report = invoke_driver(&command, b"OBSERVE\nEND\n")?; if report.contains("phase=observe status=complete interface=wg-m83-probe peers=2") && report.lines().any(|line| { line.starts_with(&format!("peer={public_key} ")) && peer_has_exact_ipv4_routes(line, &address) && line.contains("handshake=present") && peer_counters(line).is_some_and(|after| { after.0 > restored_before.0 && after.1 > restored_before.1 }) }) { restored_traffic = true; break; } thread::sleep(Duration::from_millis(250)); } if !restored_traffic { eprintln!("restored client ping summary: transmitted={transmitted} received={received}"); return Err("real runtime reader did not observe fresh restored-client IPv4 traffic"); } // Restoring the earlier baseline archive removes the restored client from // saved state while it remains active in the kernel. Its address must stay // reserved until another explicit, successfully verified fixture Apply. let reservation_restore_request = format!("RESTORE\t{baseline_archive}\t{address}\nEND\n"); let reservation_restore = invoke_driver(&command, reservation_restore_request.as_bytes())?; if !reservation_restore.contains(&format!( "phase=restore status=confirmed archive={baseline_archive} reason=pre-commit precautionary_backup=pre-restore settings=true server=true vault=true target_client_present=false pending_address_reserved=true runtime_unchanged=true" )) { return Err("restore released a pending client address before explicit Apply"); } let live_after_second_restore = invoke_driver(&command, b"OBSERVE\nEND\n")?; if !live_after_second_restore .contains("phase=observe status=complete interface=wg-m83-probe peers=2") || !live_after_second_restore.contains(&format!("peer={public_key} ")) { return Err("second restore changed the live peer set before explicit fixture Apply"); } exec_success(client, ["wg", "setconf", "wg-m85-client", remote_setconf]) .map_err(|_| "restored client fixture reconnect failed before reservation Apply")?; exec_success(client, ["ping", "-4", "-c", "2", "-W", "2", "10.20.0.1"]) .map_err(|_| "restored client access did not remain live before reservation Apply")?; let release_apply = invoke_driver( &command, format!("APPLY\t/tmp/wireguard-admin-fixture/server.conf\t{address}\nEND\n").as_bytes(), )?; if !release_apply.contains("phase=fixture_apply status=verified address_released=true") { return Err("explicit fixture Apply did not verify reservation release after restore"); } let released_runtime = invoke_driver(&command, b"OBSERVE\nEND\n")?; if !released_runtime.contains("phase=observe status=complete interface=wg-m83-probe peers=1") || released_runtime.contains(&format!("peer={public_key} ")) { return Err("explicit Apply did not remove the restored client after reservation restore"); } for _ in 0..3 { expect_fixture_ping_failure(client, "10.20.0.1")?; } let imported = invoke_driver(&command, b"IMPORT_REISSUE\nEND\n")?; for marker in [ "phase=import_reissue status=verified matched=true", "conflict_visible=true invalid_visible=true originals_unchanged=true", "wrong_password_blocked=true conflict_reissue_blocked=true reissue_mode=0600", ] { if !imported.contains(marker) { return Err("Application import/reissue scenario omitted a required safety assertion"); } } let import_temp = tempfile::Builder::new() .prefix("wga-m85-import-reissue-") .tempdir() .map_err(|_| "private import/reissue staging directory could not be created")?; std::fs::set_permissions(import_temp.path(), std::fs::Permissions::from_mode(0o700)) .map_err(|_| "private import/reissue directory permissions failed")?; let reissued = import_temp.path().join("reissued-client.conf"); docker_text_with_timeout( &[ &endpoint, "cp", &format!( "{}:/tmp/wireguard-admin-fixture/import-reissue/reissued-client.conf", server.container().id() ), reissued .to_str() .ok_or("private reissue path was not UTF-8")?, ], Duration::from_secs(20), ) .map_err(|_| "password-authorized imported client transfer failed")?; verify_private_host_file(&reissued, 64 * 1024)?; let reissued_bytes = std::fs::read(&reissued) .map_err(|_| "password-authorized imported client file could not be read")?; let (imported_address, imported_endpoint) = client_connection_material(&reissued_bytes)?; const REMOTE_REISSUED: &str = "/tmp/wgb85-imported.conf"; const REMOTE_REISSUED_SETCONF: &str = "/tmp/wgb85-imported-set.conf"; docker_text_with_timeout( &[ &endpoint, "cp", reissued .to_str() .ok_or("private reissue path was not UTF-8")?, &format!("{}:{REMOTE_REISSUED}", client.container().id()), ], Duration::from_secs(20), ) .map_err(|_| "password-authorized imported client transfer failed")?; verify_private_container_file(client, REMOTE_REISSUED)?; verify_container_copy_matches( client, REMOTE_REISSUED, &import_temp.path().join("client-copy.conf"), &reissued_bytes, &endpoint, )?; strip_client_config(client, REMOTE_REISSUED, REMOTE_REISSUED_SETCONF)?; verify_private_container_file(client, REMOTE_REISSUED_SETCONF)?; exec_success(baseline_client, ["ip", "link", "del", "wg-m85-baseline"]) .map_err(|_| "baseline client fixture did not stop before imported-key reissue")?; let _ = exec_success(client, ["ip", "link", "del", "wg-m85-client"]); setup_client_interface( client, "wg-m85-client", REMOTE_REISSUED_SETCONF, &imported_address, &imported_endpoint, )?; exec_success(client, ["ping", "-4", "-c", "3", "-W", "2", "10.20.0.1"]) .map_err(|_| "reissued imported client did not establish IPv4 tunnel traffic")?; let mut imported_traffic = false; for _ in 0..10 { let observation = invoke_driver(&command, b"OBSERVE\nEND\n")?; if observation.lines().any(|line| { line.starts_with(&format!("peer={baseline_key} ")) && peer_has_exact_ipv4_routes(line, "10.20.0.10/32") && line.contains("handshake=present") && counter_is_positive(line, "received=") && counter_is_positive(line, "transmitted=") }) { imported_traffic = true; break; } thread::sleep(Duration::from_millis(250)); } if !imported_traffic { return Err("real runtime reader did not observe traffic from the reissued imported file"); } let failure = invoke_driver(&command, b"FAILURE_PREVENTS_APPLICATION\nEND\n")?; for marker in [ "phase=reader_failures status=verified missing_interface=rejected deadline=timeout empty_or_partial=false", "phase=commit_backup_failure status=verified saved_files_unchanged=true draft_preserved=true runtime_unchanged=true archive_path_restored=true", "phase=restart_backup_failure status=verified apply_called=false restart_submitted=false address_reserved=true historical_state=unchanged runtime_unchanged=true archive_path_restored=true", "phase=failure_prevents_application status=complete", ] { if !failure.contains(marker) { return Err("backup-failure scenario omitted a required safety assertion"); } } Ok(()) } fn invoke_driver(command: &[&str], request: &[u8]) -> Result { let report = docker_exchange_report(command, request, DRIVER_TRANSPORT_TIMEOUT, 2048, 1024) .map_err(|_| "Application fixture phase exchange failed")?; let combined = format!("{}\n{}", report.stdout, report.stderr); if report.failure_category.is_some() { let progress = combined .split_whitespace() .filter(|part| { part.starts_with("phase=") || part.starts_with("status=") || part.starts_with("stage=") || part.starts_with("step=") || part.starts_with("settings_match=") || part.starts_with("server_match=") || part.starts_with("vault_match=") || part.starts_with("target_presence_match=") || part.starts_with("reservations_match=") || part.starts_with("actual_reservation_count=") || part.starts_with("expected_reservation_count=") || part.starts_with("actual_target_reserved=") || part.starts_with("expected_target_reserved=") }) .collect::>() .join(" "); eprintln!("Fixture driver reported bounded phase status: {progress}"); return Err(driver_phase_failure_category(&combined)); } if !driver_ran_exactly_one_test(&combined) { return Err("Application fixture phase driver failed"); } Ok(combined) } fn phase_public_key<'a>(report: &'a str, phase: &str) -> Result<&'a str, &'static str> { let marker = format!("phase={phase} status=verified public_key="); let create_marker = format!("phase={phase} status=ok public_key="); report .split_once(&marker) .or_else(|| report.split_once(&create_marker)) .and_then(|(_, tail)| tail.split_whitespace().next()) .ok_or("fixture phase report omitted its public identity") } fn phase_archive_name<'a>(report: &'a str, phase: &str) -> Result<&'a str, &'static str> { let completed_status = match phase { "commit" => "ok", "delete" => "saved", _ => return Err("Application phase archive identity was invalid"), }; let prefix = format!("phase={phase} "); let line = report .lines() .find(|line| { if !line.starts_with(&prefix) { return false; } let mut statuses = line .split_whitespace() .filter_map(|field| field.strip_prefix("status=")); statuses.next() == Some(completed_status) && statuses.next().is_none() }) .ok_or("Application phase omitted its archive identity")?; let mut names = line .split_whitespace() .filter_map(|field| field.strip_prefix("backup_name=")); let name = names .next() .ok_or("Application phase omitted its archive identity")?; if names.next().is_some() || name.is_empty() || name.len() > 128 || !name .bytes() .all(|byte| byte.is_ascii_alphanumeric() || b"-_.".contains(&byte)) || !name.ends_with(".tar") { return Err("Application phase archive identity was invalid"); } Ok(name) } fn verify_private_container_file( container: &OwnedContainer, path: &str, ) -> Result<(), &'static str> { let mode = exec_small(container, ["stat", "-c", "%a", path])?; if mode == "600" { Ok(()) } else { Err("secret-bearing fixture config was not mode 0600 after transfer") } } fn verify_container_copy_matches( container: &OwnedContainer, remote_path: &str, local_path: &std::path::Path, expected_bytes: &[u8], endpoint: &str, ) -> Result<(), &'static str> { docker_text_with_timeout( &[ endpoint, "cp", &format!("{}:{remote_path}", container.container().id()), local_path .to_str() .ok_or("private client verification path was not UTF-8")?, ], Duration::from_secs(20), ) .map_err(|_| "client copy could not be returned for byte verification")?; let metadata = std::fs::symlink_metadata(local_path) .map_err(|_| "client copy verification file could not be inspected")?; use std::os::unix::fs::MetadataExt; if metadata.file_type().is_symlink() || !metadata.is_file() || metadata.len() != expected_bytes.len() as u64 || metadata.mode() & 0o777 != 0o600 { return Err("client copy verification file was not a private regular file"); } let copied_bytes = std::fs::read(local_path).map_err(|_| "client copy verification file could not be read")?; if copied_bytes == expected_bytes { Ok(()) } else { Err("client copy did not match the first-delivery bytes") } } fn verify_private_host_file( path: &std::path::Path, maximum_bytes: u64, ) -> Result<(), &'static str> { use std::os::unix::fs::MetadataExt; let metadata = std::fs::symlink_metadata(path) .map_err(|_| "private transferred client file could not be inspected")?; if metadata.file_type().is_symlink() || !metadata.is_file() || metadata.len() > maximum_bytes || metadata.mode() & 0o777 != 0o600 { return Err("private transferred client file was not a bounded mode-0600 regular file"); } Ok(()) } fn strip_client_config( client: &OwnedContainer, source_path: &str, destination_path: &str, ) -> Result<(), &'static str> { // wg-quick strip parses the copied file and does not apply its hooks. The // fixed wrapper redirects private output to a mode-0600 file; config text is // never passed as an argument or emitted to the test logs. const STRIP_SCRIPT: &str = "umask 077; wg-quick strip \"$1\" > \"$2\" && chmod 600 \"$2\""; exec_success( client, [ "/bin/busybox", "sh", "-c", STRIP_SCRIPT, "wga-strip", source_path, destination_path, ], ) .map_err(|_| "copied client configuration could not be safely stripped") } fn setup_client_interface( client: &OwnedContainer, interface: &str, setconf_path: &str, address: &str, endpoint: &str, ) -> Result<(), &'static str> { if interface != "wg-m85-baseline" && interface != "wg-m85-client" { return Err("client fixture interface name was not recognized"); } let endpoint_route = endpoint_host_route(endpoint)?; exec_success( client, ["ip", "link", "add", interface, "type", "wireguard"], ) .map_err(|_| "client fixture WireGuard interface setup failed")?; // The endpoint has a more-specific route through the underlay before the // default route is sent through WireGuard. exec_success( client, [ "ip", "-4", "route", "replace", &endpoint_route, "dev", "eth0", ], ) .map_err(|_| "server endpoint underlay route could not be preserved")?; exec_success(client, ["wg", "setconf", interface, setconf_path]) .map_err(|_| "delivered client configuration was rejected by WireGuard")?; exec_success( client, ["ip", "-4", "address", "add", address, "dev", interface], ) .map_err(|_| "delivered IPv4 address could not be configured")?; exec_success(client, ["ip", "link", "set", "dev", interface, "up"]) .map_err(|_| "client fixture WireGuard link setup failed")?; exec_success( client, ["ip", "-4", "route", "replace", "default", "dev", interface], ) .map_err(|_| "delivered full-tunnel IPv4 route could not be configured") } fn endpoint_host_route(endpoint: &str) -> Result { let (host, port) = endpoint .rsplit_once(':') .ok_or("delivered client endpoint was malformed")?; if port != "51820" { return Err("delivered client endpoint did not use the fixture port"); } let address = host .parse::() .map_err(|_| "delivered client endpoint was not IPv4")?; Ok(format!("{address}/32")) } fn expect_fixture_ping_failure( container: &OwnedContainer, destination: &str, ) -> Result<(), &'static str> { require_zero_ping_replies(bounded_ping_summary(container, destination)) } fn require_zero_ping_replies( summary: Result<(u32, u32), &'static str>, ) -> Result<(), &'static str> { let (transmitted, received) = summary?; if transmitted == 3 && received == 0 { Ok(()) } else if received > 0 { Err("client still reached the tunnel during a negative reachability probe") } else { Err("negative reachability probe did not transmit three packets") } } fn bounded_ping_summary( container: &OwnedContainer, destination: &str, ) -> Result<(u32, u32), &'static str> { // Keep ping's packet-loss summary even when it exits nonzero; the final // `true` only preserves that bounded output for diagnosis, while the // parsed receive count below remains a strict reachability assertion. const PING_SCRIPT: &str = "ping -q -4 -c 3 -W 2 \"$1\"; /bin/busybox true"; let output = exec_bounded( container, &[ "/bin/busybox", "sh", "-c", PING_SCRIPT, "wga-ping", destination, ], COMMAND_STDOUT_LIMIT, COMMAND_STDERR_LIMIT, "Baseline reachability probe failed", )?; let output = String::from_utf8(output).map_err(|_| "Baseline ping output was invalid")?; let summary = output .lines() .find(|line| line.contains("packets transmitted") && line.contains("packets received")) .ok_or("Baseline ping summary was absent")?; parse_ping_summary(summary) } fn parse_ping_summary(summary: &str) -> Result<(u32, u32), &'static str> { let mut fields = summary.split(','); let transmitted = fields .next() .filter(|field| field.contains("packets transmitted")) .and_then(|field| field.split_whitespace().next()) .and_then(|value| value.parse::().ok()) .ok_or("Baseline ping summary was malformed")?; let received = fields .next() .filter(|field| field.contains("packets received")) .and_then(|field| field.split_whitespace().next()) .and_then(|value| value.parse::().ok()) .ok_or("Baseline ping summary was malformed")?; Ok((transmitted, received)) } fn report_baseline_failure( server: &OwnedContainer, command: &[&str], baseline_key: &str, stage: &str, ) -> Result<(), &'static str> { let report = invoke_driver(command, b"OBSERVE\nEND\n")?; let peer = report .lines() .find(|line| line.starts_with(&format!("peer={baseline_key} "))); if let Some(peer) = peer { let routes = peer .split_whitespace() .find_map(|part| part.strip_prefix("routes=")) .unwrap_or("unavailable"); let handshake = peer .split_whitespace() .find_map(|part| part.strip_prefix("handshake=")) .unwrap_or("unavailable"); let received = peer .split_whitespace() .find_map(|part| part.strip_prefix("received=")) .unwrap_or("unavailable"); let transmitted = peer .split_whitespace() .find_map(|part| part.strip_prefix("transmitted=")) .unwrap_or("unavailable"); eprintln!( "baseline peer {stage}: routes={routes} handshake={handshake} received={received} transmitted={transmitted}" ); } else { eprintln!("baseline peer {stage}: absent from complete runtime observation"); } let endpoints = exec_bounded( server, &["wg", "show", "wg-m83-probe", "endpoints"], COMMAND_STDOUT_LIMIT, COMMAND_STDERR_LIMIT, "Baseline endpoint observation failed", )?; let endpoints = String::from_utf8(endpoints).map_err(|_| "Baseline endpoint observation was invalid")?; let endpoint_learned = endpoints.lines().any(|line| { line.split_whitespace() .next() .is_some_and(|identity| identity == baseline_key) && line .split_whitespace() .nth(1) .is_some_and(|endpoint| endpoint != "(none)") }); eprintln!("baseline server endpoint learned={endpoint_learned}"); Ok(()) } fn counter_is_positive(peer: &str, field: &str) -> bool { peer.split_whitespace() .find_map(|part| part.strip_prefix(field)) .and_then(|value| value.parse::().ok()) .is_some_and(|value| value > 0) } fn peer_counters(peer: &str) -> Option<(u64, u64)> { let received = peer .split_whitespace() .find_map(|part| part.strip_prefix("received="))? .parse::() .ok()?; let transmitted = peer .split_whitespace() .find_map(|part| part.strip_prefix("transmitted="))? .parse::() .ok()?; Some((received, transmitted)) } fn client_connection_material(bytes: &[u8]) -> Result<(String, String), &'static str> { let text = std::str::from_utf8(bytes).map_err(|_| "delivered client file was not UTF-8")?; let mut section = ""; let mut addresses = Vec::new(); let mut endpoint = None; let mut ipv4_routes = std::collections::BTreeSet::new(); for line in text.lines() { let trimmed = line.trim(); if trimmed.starts_with('[') && trimmed.ends_with(']') { section = trimmed; } if let Some((name, value)) = trimmed.split_once('=') { match (section, name.trim()) { ("[Interface]", "Address") => { for item in value.split(',') { let address = item .trim() .parse::() .map_err(|_| "delivered client Address was malformed")?; if let ipnet::IpNet::V4(network) = address { addresses.push(network); } } } ("[Peer]", "Endpoint") => endpoint = Some(value.trim().to_owned()), ("[Peer]", "AllowedIPs") => { for item in value.split(',') { let route = item .trim() .parse::() .map_err(|_| "delivered client AllowedIPs was malformed")?; if let ipnet::IpNet::V4(network) = route { ipv4_routes.insert(network); } } } _ => {} } } } let expected_route = "0.0.0.0/0" .parse::() .map_err(|_| "expected IPv4 full-tunnel route was invalid")?; if ipv4_routes != [expected_route].into_iter().collect() { return Err("delivered client file did not contain the expected IPv4 full-tunnel route"); } let endpoint = endpoint.ok_or("delivered client endpoint was absent")?; let _ = endpoint_host_route(&endpoint)?; if addresses.len() != 1 || addresses[0].prefix_len() != 32 { return Err("delivered client did not contain exactly one IPv4 /32 address"); } Ok((addresses[0].to_string(), endpoint)) } fn peer_has_exact_ipv4_routes(peer: &str, expected: &str) -> bool { let Some(routes) = peer .split_whitespace() .find_map(|part| part.strip_prefix("routes=")) else { return false; }; let actual = routes .split(',') .filter_map(|route| route.parse::().ok()) .filter_map(|route| match route { ipnet::IpNet::V4(route) => Some(route), ipnet::IpNet::V6(_) => None, }) .collect::>(); let Ok(expected) = expected.parse::() else { return false; }; actual == [expected].into_iter().collect() } #[derive(Debug, Eq, PartialEq)] struct DockerExchangeReport { stdout: String, stderr: String, failure_category: Option<&'static str>, } fn docker_exchange_report( arguments: &[&str], request: &[u8], command_deadline: Duration, stdout_limit: usize, stderr_limit: usize, ) -> Result { if request.len() > MAX_PHASE_REQUEST_BYTES { return Err("fixture phase request exceeded its limit"); } let mut child = Command::new("docker") .args(arguments) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .spawn() .map_err(|_| "Docker CLI could not be started")?; let mut stdin = child.stdin.take().ok_or("Docker stdin unavailable")?; let stdout = child.stdout.take().ok_or("Docker stdout unavailable")?; let stderr = child.stderr.take().ok_or("Docker stderr unavailable")?; let stdout_reader = thread::spawn(move || read_bounded(stdout, stdout_limit)); let stderr_reader = thread::spawn(move || read_bounded(stderr, stderr_limit)); let deadline = Instant::now() + command_deadline; if stdin.write_all(request).is_err() { let _ = child.kill(); let _ = child.wait(); let _ = stdout_reader.join(); let _ = stderr_reader.join(); return Err("Docker fixture request write failed"); } drop(stdin); let status = loop { match child.try_wait() { Ok(Some(status)) => break status, Ok(None) if Instant::now() >= deadline => { let _ = child.kill(); let _ = child.wait(); let _ = stdout_reader.join(); let _ = stderr_reader.join(); return Err("Docker command timed out"); } Ok(None) => thread::sleep(Duration::from_millis(20)), Err(_) => { let _ = child.kill(); let _ = child.wait(); let _ = stdout_reader.join(); let _ = stderr_reader.join(); return Err("Docker command wait failed"); } } }; let (stdout, stdout_exceeded) = stdout_reader .join() .map_err(|_| "Docker stdout reader failed")? .map_err(|_| "Docker stdout read failed")?; let (stderr, stderr_exceeded) = stderr_reader .join() .map_err(|_| "Docker stderr reader failed")? .map_err(|_| "Docker stderr read failed")?; if stdout_exceeded || stderr_exceeded { return Err("Docker command output exceeded its limit"); } let stdout = String::from_utf8(stdout).map_err(|_| "Docker returned non-UTF-8 output")?; let stderr = String::from_utf8(stderr).map_err(|_| "Docker returned non-UTF-8 output")?; let failure_category = (!status.success()).then(|| match status.code() { Some(124 | 137 | 143) => "In-container fixture command timed out", _ => "Docker command returned a nonzero status", }); Ok(DockerExchangeReport { stdout, stderr, failure_category, }) } fn read_bounded(mut reader: impl Read, limit: usize) -> std::io::Result<(Vec, bool)> { let mut captured = Vec::new(); let mut buffer = [0_u8; 1024]; let mut exceeded = false; loop { let count = reader.read(&mut buffer)?; if count == 0 { return Ok((captured, exceeded)); } let remaining = limit.saturating_sub(captured.len()); captured.extend_from_slice(&buffer[..count.min(remaining)]); exceeded |= count > remaining; } } fn driver_ran_exactly_one_test(output: &str) -> bool { output.contains("running 1 test") && output.contains("test container_driver::alpine_scenario_driver") } fn driver_phase_failure_category(output: &str) -> &'static str { if let Some(stage) = output.lines().find_map(|line| { line.split_once("phase=fixture_apply status=failed stage=") .map(|(_, tail)| tail.trim()) }) { return match stage { "Preparation" => "Application fixture Apply failed during preparation", "Backup" => "Application fixture Apply failed during backup", "Service" => "Application fixture Apply command failed", "RuntimeRead" => "Application fixture Apply runtime observation failed", "Verification" => "Application fixture Apply peer verification failed", "Persistence" => "Application fixture Apply saved-state publication failed", _ => "Application fixture Apply failed at an unknown stage", }; } let failed_phase = output .split_whitespace() .collect::>() .windows(2) .filter_map(|pair| { (pair[1] == "status=started") .then(|| pair[0].strip_prefix("phase=")) .flatten() }) .next_back(); match failed_phase { Some("create") => "Application fixture Create phase failed", Some("commit") => "Application fixture Commit phase failed", Some("delivery") => "Application fixture delivery phase failed", Some("delete") => "Application fixture saved deletion phase failed", Some("baseline_apply") => "Application fixture baseline setup failed", Some("fixture_apply") => "Application fixture apply phase failed", Some("failure_prevents_application") => "Application fixture backup-failure phase failed", Some("reader_failures") => "Application fixture runtime-reader failure checks failed", Some("commit_backup_failure") => "Application fixture Commit backup-failure check failed", Some("restart_backup_failure") => "Application fixture restart backup-failure check failed", Some("end") => "Application fixture protocol termination failed", _ => "Application fixture bootstrap failed", } } fn missing_driver_phase_category(output: &str) -> Option<&'static str> { let has = |marker: &str| output.split_once(marker).is_some(); if !has("phase=fixture_bootstrap status=ok") { return Some("Application fixture report omitted bootstrap completion"); } if !has("phase=create status=ok public_key=") { return Some("Application fixture report omitted Create completion"); } if !has("phase=commit status=ok") { return Some("Application fixture report omitted Commit completion"); } if !has("phase=delivery status=ok activation_warning=true") { return Some("Application fixture report omitted first delivery or activation warning"); } if !has("current_record=removed backup_record=retained address_reserved=true") { return Some("Application fixture report omitted saved-deletion retention checks"); } if !has("phase=restore status=confirmed") || !has("target_client_present=true pending_address_reserved=false") || !has("target_client_present=false pending_address_reserved=true") || !has("precautionary_backup=pre-restore") { return Some("Application fixture report omitted password-free restore separation checks"); } if !has("phase=fixture_apply status=verified address_released=true") { return Some("Application fixture report omitted verified fixture apply"); } if !has("phase=end status=ok") { return Some("Application fixture report omitted protocol termination"); } None } fn exec_smoke_command( container: &OwnedContainer, arguments: &[&str; N], category: &'static str, ) -> Result<(), &'static str> { exec_bounded( container, arguments, COMMAND_STDOUT_LIMIT, COMMAND_STDERR_LIMIT, category, ) .map(|_| ()) } /// Cleans exact resources only after their name and per-worker owner label match. /// The caller must stop and reap the worker first, preventing later creation. pub fn cleanup_confirmed_probe_resources( selection: &Selection, names: &ProbeNames, owner: &str, budget: Duration, ) -> Vec<&'static str> { let deadline = Instant::now() + budget; let mut failures = Vec::new(); for (kind, name) in [ (ResourceKind::Container, names.client.as_str()), (ResourceKind::Container, names.baseline.as_str()), (ResourceKind::Container, names.server.as_str()), (ResourceKind::Network, names.network.as_str()), ] { if let Err(category) = remove_if_confirmed_owned(selection, kind, name, owner, deadline) { failures.push(category); } } failures } #[derive(Clone, Copy)] enum ResourceKind { Container, Network, } fn remove_if_confirmed_owned( selection: &Selection, kind: ResourceKind, name: &str, owner: &str, deadline: Instant, ) -> Result<(), &'static str> { let timeout = deadline .checked_duration_since(Instant::now()) .filter(|remaining| !remaining.is_zero()) .ok_or("cleanup budget expired")?; let endpoint = format!("--host={}", selection.endpoint); let (resource, format) = match kind { ResourceKind::Container => ( "container", "--format={{.Id}} {{index .Config.Labels \"wireguard-admin.m8_3_owner\"}}", ), ResourceKind::Network => ( "network", "--format={{.Id}} {{index .Labels \"wireguard-admin.m8_3_owner\"}}", ), }; let output = match docker_text_with_timeout( &[&endpoint, resource, "inspect", format, name], timeout.min(Duration::from_secs(5)), ) { Ok(output) => output, Err("Docker named resource was not found") => return Ok(()), Err(_) => return Err("owned resource status could not be confirmed"), }; let id = owned_resource_id(&output, owner) .ok_or("resource name exists without the expected ownership label")?; let timeout = deadline .checked_duration_since(Instant::now()) .filter(|remaining| !remaining.is_zero()) .ok_or("cleanup budget expired")?; let removal = match kind { ResourceKind::Container => docker_text_with_timeout( &[&endpoint, "container", "rm", "--force", &id], timeout.min(Duration::from_secs(5)), ), ResourceKind::Network => docker_text_with_timeout( &[&endpoint, "network", "rm", &id], timeout.min(Duration::from_secs(5)), ), }; removal .map(|_| ()) .map_err(|_| "confirmed owned resource could not be removed") } fn owned_resource_id(output: &str, expected_owner: &str) -> Option { let (id, actual_owner) = output.trim().split_once(' ')?; if actual_owner != expected_owner || id.len() < 12 || id.len() > 64 || !id.bytes().all(|byte| byte.is_ascii_hexdigit()) { return None; } Some(id.to_owned()) } fn verify_daemon(selection: &Selection) -> Result<(), &'static str> { let output = docker_text(&["--host", &selection.endpoint, "info", "--format={{.ID}}"])?; if output.trim() != selection.daemon_id { return Err("selected daemon ID does not match the live daemon"); } Ok(()) } fn verify_cached_image(selection: &Selection) -> Result<(), &'static str> { let endpoint = format!("--host={}", selection.endpoint); let image = "--format={{.Id}} {{.Os}}/{{.Architecture}}"; let output = docker_text(&[&endpoint, "image", "inspect", image, IMAGE])?; if output.trim() != format!("{IMAGE_ID} linux/amd64") { return Err("cached Alpine fixture image identity does not match the pin"); } Ok(()) } fn start_fixture_container( selection: &Selection, name: &str, network: &str, ) -> Result { let owner = std::process::id().to_string(); let container = fixture_container_request(name, network, owner.clone()).start(); let container = match container { Ok(container) => container, Err(error) => { let category = testcontainers_start_failure_category(&error); match partial_start_status(find_owned_container(selection, name, &owner)) { PartialStartStatus::ConfirmedOwned(id) => { let endpoint = format!("--host={}", selection.endpoint); if docker_text(&[&endpoint, "container", "rm", "--force", &id]).is_err() { eprintln!( "Testcontainers cleanup incomplete; owned container may remain: {name}" ); } } PartialStartStatus::Uncertain => eprintln!( "Testcontainers start failed; resource status remains uncertain after unconfirmed exact-name inspection: {name}" ), } return Err(category); } }; Ok(OwnedContainer { name: name.to_owned(), selection: selection.clone(), container: Some(container), }) } fn fixture_container_request( name: &str, network: &str, owner: String, ) -> testcontainers::ContainerRequest { GenericImage::new("wireguard-admin-alpine", "3.22.7") .with_entrypoint("/bin/sh") .with_cmd(["-c", "while :; do sleep 1; done"]) .with_container_name(name) .with_network(network) .with_cap_add("NET_ADMIN") .with_label("wireguard-admin.m8_3_owner", owner) .with_startup_timeout(CONTAINER_STARTUP_TIMEOUT) } enum PartialStartStatus { ConfirmedOwned(String), Uncertain, } fn partial_start_status(inspection: Result, &'static str>) -> PartialStartStatus { match inspection { Ok(Some(id)) => PartialStartStatus::ConfirmedOwned(id), Ok(None) | Err(_) => PartialStartStatus::Uncertain, } } fn testcontainers_start_failure_category(error: &TestcontainersError) -> &'static str { match error { TestcontainersError::Client(ClientError::Init(_)) => { "Testcontainers Docker client initialization failed" } TestcontainersError::Client(ClientError::CreateContainer(_)) => { "Testcontainers container creation failed" } TestcontainersError::Client(ClientError::StartContainer(_)) => { "Testcontainers container start failed" } TestcontainersError::WaitContainer(WaitContainerError::StartupTimeout) => { "Testcontainers container readiness timed out" } TestcontainersError::WaitContainer(_) => "Testcontainers container readiness failed", TestcontainersError::Client(ClientError::Configuration(_)) => { "Testcontainers Docker client configuration failed" } TestcontainersError::Client(ClientError::InvalidDockerHost(_)) => { "Testcontainers Docker host configuration failed" } TestcontainersError::Client(ClientError::PullImage { .. }) => { "Testcontainers container creation not-found image fallback failed" } TestcontainersError::Client(ClientError::BuildImage { .. }) => { "Testcontainers fixture image build failed" } TestcontainersError::Client(ClientError::PortMapping(_)) => { "Testcontainers container port mapping failed" } TestcontainersError::Client(ClientError::ListContainers(_)) => { "Testcontainers container listing failed" } TestcontainersError::Client(ClientError::RemoveContainer(_)) => { "Testcontainers container removal failed" } TestcontainersError::Client(ClientError::StopContainer(_)) => { "Testcontainers container stop failed" } TestcontainersError::Client(ClientError::PauseContainer(_)) => { "Testcontainers container pause failed" } TestcontainersError::Client(ClientError::UnpauseContainer(_)) => { "Testcontainers container resume failed" } TestcontainersError::Client(ClientError::InspectContainer(_)) => { "Testcontainers container inspection failed" } TestcontainersError::Client(ClientError::CreateNetwork(_)) => { "Testcontainers network creation failed" } TestcontainersError::Client(ClientError::InspectNetwork(_)) => { "Testcontainers network inspection failed" } TestcontainersError::Client(ClientError::ListNetworks(_)) => { "Testcontainers network listing failed" } TestcontainersError::Client(ClientError::RemoveNetwork(_)) => { "Testcontainers network removal failed" } TestcontainersError::Client(ClientError::InitExec(_)) => { "Testcontainers exec initialization failed" } TestcontainersError::Client(ClientError::InspectExec(_)) => { "Testcontainers exec inspection failed" } TestcontainersError::Client(ClientError::UploadToContainerError(_)) => { "Testcontainers fixture upload failed" } TestcontainersError::Client(ClientError::CopyToContainerError(_)) => { "Testcontainers fixture preparation failed" } TestcontainersError::Client(ClientError::CopyFromContainerError(_)) => { "Testcontainers fixture download failed" } TestcontainersError::PortNotExposed { .. } => { "Testcontainers container configuration failed" } TestcontainersError::MissingInfo(_) => "Testcontainers container information unavailable", TestcontainersError::Exec(_) => "Testcontainers container execution failed", TestcontainersError::Io(_) => "Testcontainers host I/O failed", TestcontainersError::Other(_) => "Testcontainers container startup failed", } } fn find_owned_container( selection: &Selection, name: &str, expected_owner: &str, ) -> Result, &'static str> { let endpoint = format!("--host={}", selection.endpoint); let format = "--format={{.Id}} {{index .Config.Labels \"wireguard-admin.m8_3_owner\"}}"; let output = docker_text(&[&endpoint, "container", "inspect", format, name])?; let Some((id, owner)) = output.trim().split_once(' ') else { return Ok(None); }; if owner != expected_owner || id.len() < 12 || id.len() > 64 || !id.bytes().all(|byte| byte.is_ascii_hexdigit()) { return Ok(None); } Ok(Some(id.to_owned())) } fn verify_distinct_namespaces( server: &OwnedContainer, client: &OwnedContainer, ) -> Result<(), &'static str> { let server_namespace = exec_small(server, ["readlink", "/proc/self/ns/net"])?; let client_namespace = exec_small(client, ["readlink", "/proc/self/ns/net"])?; if server_namespace.is_empty() || server_namespace == client_namespace { return Err("server and client do not have distinct network namespaces"); } Ok(()) } fn verify_wireguard_and_ipv4(container: &OwnedContainer) -> Result<(), &'static str> { exec_success( container, ["ip", "link", "add", "wg-m83-probe", "type", "wireguard"], )?; exec_success( container, ["ip", "-4", "addr", "add", "192.0.2.1/32", "dev", "lo"], )?; const ROUTE: &str = "198.51.100.0/24"; exec_success(container, ["ip", "-4", "route", "add", "blackhole", ROUTE])?; let routes = exec_small(container, ["ip", "-4", "route", "show", "exact", ROUTE])?; if !ipv4_blackhole_route_present(&routes, ROUTE) { return Err("IPv4 route mutation was not visible in the fixture namespace"); } exec_success(container, ["ip", "-4", "route", "del", "blackhole", ROUTE])?; let routes = exec_small(container, ["ip", "-4", "route", "show", "exact", ROUTE])?; if !ipv4_route_absent(&routes, ROUTE) { return Err("IPv4 fixture route remained after deletion"); } Ok(()) } fn ipv4_blackhole_route_present(routes: &str, prefix: &str) -> bool { let expected = format!("blackhole {prefix}"); routes.lines().any(|line| line.starts_with(&expected)) } fn ipv4_route_absent(routes: &str, prefix: &str) -> bool { !routes .lines() .any(|line| line.split_whitespace().any(|part| part == prefix)) } fn exec_success( container: &OwnedContainer, arguments: [&str; N], ) -> Result<(), &'static str> { exec_bounded( container, &arguments, COMMAND_STDOUT_LIMIT, COMMAND_STDERR_LIMIT, "WireGuard or IPv4 capability probe failed", ) .map(|_| ()) } fn exec_small( container: &OwnedContainer, arguments: [&str; N], ) -> Result { let output = exec_bounded( container, &arguments, NAMESPACE_STDOUT_LIMIT, COMMAND_STDERR_LIMIT, "Testcontainers namespace probe returned nonzero status", )?; String::from_utf8(output) .map(|value| value.trim().to_owned()) .map_err(|_| "Testcontainers namespace output was invalid") } fn bounded_command<'a>(arguments: &[&'a str]) -> Vec<&'a str> { let mut bounded = vec![ "/bin/busybox", "timeout", "-s", "KILL", FIXTURE_COMMAND_TIMEOUT_SECONDS, ]; bounded.extend_from_slice(arguments); bounded } fn exec_bounded( container: &OwnedContainer, arguments: &[&str; N], stdout_limit: usize, stderr_limit: usize, failure_category: &'static str, ) -> Result, &'static str> { let endpoint = format!("--host={}", container.selection.endpoint); let mut command = vec![endpoint.as_str(), "exec", container.container().id()]; command.extend(bounded_command(arguments)); match docker_text_with_timeout_and_limits( &command, FIXTURE_TRANSPORT_TIMEOUT, stdout_limit, stderr_limit, ) { Ok(stdout) => Ok(stdout.into_bytes()), Err("Docker command timed out") => { eprintln!( "Docker exec timed out; command completion inside the owned container is uncertain: {}", container.name ); Err("Fixture command timed out; completion inside the owned container is uncertain") } Err("In-container fixture command timed out") => { Err("In-container fixture command timed out") } Err("Docker command output exceeded its limit") => { Err("Fixture command output exceeded its limit") } Err("Docker command returned a nonzero status") => Err(failure_category), Err(_) => Err(failure_category), } } fn find_owned_network( selection: &Selection, name: &str, expected_owner: &str, ) -> Result, &'static str> { let endpoint = format!("--host={}", selection.endpoint); let format = "--format={{.Id}} {{index .Labels \"wireguard-admin.m8_3_owner\"}}"; let output = docker_text(&[&endpoint, "network", "inspect", format, name])?; let Some((id, owner)) = output.trim().split_once(' ') else { return Ok(None); }; if owner != expected_owner || id.len() < 12 || id.len() > 64 || !id.bytes().all(|byte| byte.is_ascii_hexdigit()) { return Ok(None); } Ok(Some(id.to_owned())) } struct OwnedNetwork { selection: Selection, name: String, id: String, removed: bool, } struct OwnedContainer { name: String, selection: Selection, container: Option>, } impl OwnedContainer { fn container(&self) -> &testcontainers::Container { self.container .as_ref() .expect("owned fixture container is present before cleanup") } fn remove(mut self) -> Result<(), &'static str> { if let Some(container) = self.container.take() && container.rm().is_err() { eprintln!( "Testcontainers cleanup incomplete; owned container may remain: {}", self.name ); return Err("Testcontainers could not remove an owned fixture container"); } Ok(()) } } impl Drop for OwnedContainer { fn drop(&mut self) { if let Some(container) = self.container.take() && container.rm().is_err() { eprintln!( "Testcontainers cleanup incomplete; owned container may remain: {}", self.name ); } } } impl OwnedNetwork { fn create(selection: &Selection, name: String) -> Result { let owner = format!("wireguard-admin.m8_3_owner={}", std::process::id()); let create_result = docker_text(&[ "--host", &selection.endpoint, "network", "create", "--driver", "bridge", "--internal", "--subnet", NETWORK_IPV4, "--label", &owner, &name, ]); let id = match create_result { Ok(id) => id, Err("Docker command timed out") => { match find_owned_network(selection, &name, &std::process::id().to_string()) { Ok(Some(id)) => id, Ok(None) => { eprintln!( "Docker network creation timed out; exact resource status is unconfirmed: {name}" ); return Err("Docker network creation timed out without an owned network"); } Err(_) => { eprintln!( "Docker network creation timed out; exact resource status is unknown: {name}" ); return Err("Docker network creation timed out; cleanup status is unknown"); } } } Err(error) => return Err(error), }; let id = id.trim(); if id.len() < 12 || id.len() > 64 || !id.bytes().all(|byte| byte.is_ascii_hexdigit()) { return Err("Docker returned an invalid ID for the owned test network"); } Ok(Self { selection: selection.clone(), name, id: id.to_owned(), removed: false, }) } fn remove(mut self) -> Result<(), &'static str> { self.remove_inner()?; Ok(()) } fn remove_inner(&mut self) -> Result<(), &'static str> { if self.removed { return Ok(()); } let endpoint = format!("--host={}", self.selection.endpoint); docker_text(&[&endpoint, "network", "rm", &self.id])?; self.removed = true; Ok(()) } } impl Drop for OwnedNetwork { fn drop(&mut self) { if !self.removed && self.remove_inner().is_err() { eprintln!( "Testcontainers cleanup incomplete; owned network may remain: {}", self.name ); } } } #[cfg(test)] mod tests { use super::{ IMAGE, MAX_PHASE_REQUEST_BYTES, PartialStartStatus, ProbeNames, bounded_command, client_connection_material, docker_exchange_report, driver_phase_failure_category, driver_ran_exactly_one_test, endpoint_host_route, fixture_container_request, ipv4_blackhole_route_present, ipv4_route_absent, missing_driver_phase_category, owned_resource_id, parse_ping_summary, partial_start_status, peer_counters, peer_has_exact_ipv4_routes, phase_archive_name, require_zero_ping_replies, testcontainers_start_failure_category, }; use std::io; use std::time::Duration; use testcontainers::{ TestcontainersError, core::error::{ClientError, WaitContainerError}, }; #[test] fn successful_driver_report_requires_restore_and_address_lifecycle_evidence() { let report = "test container_driver::alpine_scenario_driver ... phase=fixture_bootstrap status=ok\nphase=create status=ok public_key=public\nphase=commit status=ok\nphase=delivery status=ok activation_warning=true\nphase=delete status=saved current_record=removed backup_record=retained address_reserved=true\nphase=restore status=confirmed precautionary_backup=pre-restore target_client_present=true pending_address_reserved=false\nphase=restore status=confirmed precautionary_backup=pre-restore target_client_present=false pending_address_reserved=true\nphase=fixture_apply status=verified address_released=true\nphase=end status=ok\n"; assert_eq!(missing_driver_phase_category(report), None); } #[test] fn phase_archive_name_skips_started_commit_and_delete_lines() { let report = "phase=commit status=started\nphase=commit status=ok backup_name=commit-123.tar\nphase=delete status=started backup_name=wrong.tar\nphase=delete status=saved backup_name=delete-456.tar"; assert_eq!(phase_archive_name(report, "commit"), Ok("commit-123.tar")); assert_eq!(phase_archive_name(report, "delete"), Ok("delete-456.tar")); } #[test] fn phase_archive_name_rejects_missing_or_malformed_completed_archive() { assert_eq!( phase_archive_name( "phase=commit status=started backup_name=commit-123.tar", "commit" ), Err("Application phase omitted its archive identity") ); assert_eq!( phase_archive_name("phase=delete status=saved", "delete"), Err("Application phase omitted its archive identity") ); assert_eq!( phase_archive_name("phase=commit status=ok backup_name=../escape.tar", "commit"), Err("Application phase archive identity was invalid") ); assert_eq!( phase_archive_name( "phase=delete status=saved backup_name=first.tar backup_name=second.tar", "delete" ), Err("Application phase archive identity was invalid") ); } #[test] fn driver_report_rejects_zero_test_exact_filter_mismatch() { assert!(!driver_ran_exactly_one_test( "running 0 tests\ntest result: ok. 0 passed; 0 failed" )); assert!(driver_ran_exactly_one_test( "running 1 test\ntest container_driver::alpine_scenario_driver ... phase=fixture_bootstrap" )); } #[test] fn host_phase_transport_rejects_oversized_request_before_starting_docker() { let result = docker_exchange_report( &["--version"], &vec![b'x'; MAX_PHASE_REQUEST_BYTES + 1], Duration::from_secs(1), 16, 16, ); assert_eq!(result, Err("fixture phase request exceeded its limit")); } #[test] fn delivered_config_material_keeps_wireguard_fields_and_ipv4_only_setup() { let bytes = b"[Interface]\nPrivateKey = synthetic\nAddress = 10.20.0.2/32, fd20::2/128\nDNS = 10.20.0.1\n\n[Peer]\nPublicKey = synthetic-server\nAllowedIPs = 0.0.0.0/0, ::/0\nEndpoint = 192.168.253.2:51820\n"; let (address, endpoint) = client_connection_material(bytes).expect("synthetic delivery should parse"); assert_eq!(address, "10.20.0.2/32"); assert_eq!(endpoint, "192.168.253.2:51820"); } #[test] fn delivered_config_rejects_extra_ipv4_full_tunnel_prefixes() { let bytes = b"[Interface]\nPrivateKey = synthetic\nAddress = 10.20.0.2/32\n\n[Peer]\nPublicKey = server\nAllowedIPs = 0.0.0.0/0, 10.20.0.0/24, ::/0\nEndpoint = 192.168.253.2:51820\n"; assert_eq!( client_connection_material(bytes), Err("delivered client file did not contain the expected IPv4 full-tunnel route") ); } #[test] fn observed_peer_route_check_rejects_extra_ipv4_prefixes_but_ignores_ipv6() { assert!(peer_has_exact_ipv4_routes( "peer=key routes=10.20.0.10/32,fd20::10/128", "10.20.0.10/32" )); assert!(!peer_has_exact_ipv4_routes( "peer=key routes=10.20.0.10/32,10.20.0.0/24,fd20::10/128", "10.20.0.10/32" )); } #[test] fn delivered_config_material_rejects_missing_ipv4_full_tunnel_route() { let bytes = b"[Interface]\nPrivateKey = synthetic\nAddress = fd20::2/128\n\n[Peer]\nPublicKey = server\nAllowedIPs = ::/0\nEndpoint = 192.168.253.2:51820\n"; assert_eq!( client_connection_material(bytes), Err("delivered client file did not contain the expected IPv4 full-tunnel route") ); } #[test] fn server_endpoint_keeps_a_host_route_on_the_underlay() { assert_eq!( endpoint_host_route("192.168.253.4:51820"), Ok("192.168.253.4/32".to_owned()) ); assert_eq!( endpoint_host_route("vpn.example.test:51820"), Err("delivered client endpoint was not IPv4") ); } #[test] fn reader_counter_parser_requires_both_numeric_directions() { assert_eq!( peer_counters("peer=public received=12 transmitted=34"), Some((12, 34)) ); assert_eq!( peer_counters("peer=public received=unavailable transmitted=34"), None ); assert_eq!( peer_counters("peer=public received=12 transmitted=unavailable"), None ); } #[test] fn baseline_ping_summary_parses_busybox_packet_counts() { assert_eq!( parse_ping_summary("3 packets transmitted, 2 packets received, 33% packet loss"), Ok((3, 2)) ); assert_eq!( parse_ping_summary("3 packets transmitted, 0 packets received, 100% packet loss"), Ok((3, 0)) ); assert_eq!( parse_ping_summary("packets transmitted, 0 packets received, 100% packet loss"), Err("Baseline ping summary was malformed") ); } #[test] fn negative_reachability_requires_three_transmits_and_zero_replies() { assert_eq!(require_zero_ping_replies(Ok((3, 0))), Ok(())); assert_eq!( require_zero_ping_replies(Ok((3, 1))), Err("client still reached the tunnel during a negative reachability probe") ); assert_eq!( require_zero_ping_replies(Ok((0, 0))), Err("negative reachability probe did not transmit three packets") ); assert_eq!( require_zero_ping_replies(Err("Baseline reachability probe failed")), Err("Baseline reachability probe failed") ); assert_eq!( require_zero_ping_replies(Err("In-container fixture command timed out")), Err("In-container fixture command timed out") ); } #[test] fn failed_driver_report_maps_only_the_last_typed_phase() { assert_eq!( driver_phase_failure_category( "test container_driver::alpine_scenario_driver ... phase=fixture_bootstrap status=ok\nphase=create status=ok public_key=synthetic\nphase=commit status=started\n" ), "Application fixture Commit phase failed" ); assert_eq!( driver_phase_failure_category("secret-like malformed output"), "Application fixture bootstrap failed" ); } #[test] fn generated_resource_names_are_unique_and_scoped() { let first = ProbeNames::unique(); let second = ProbeNames::unique(); assert_ne!(first.network, second.network); assert!(first.network.starts_with("wga-m83-net-")); assert!(first.server.starts_with("wga-m83-server-")); assert!(first.baseline.starts_with("wga-m85-baseline-")); assert!(first.client.starts_with("wga-m83-client-")); assert_ne!(first.baseline, first.client); } #[test] fn fixture_request_keeps_cached_image_and_sets_owned_unique_container_name() { let first_name = "wga-m83-server-test-a"; let second_name = "wga-m83-client-test-b"; let first = fixture_container_request(first_name, "wga-m83-net-test", "owner-a".into()); let second = fixture_container_request(second_name, "wga-m83-net-test", "owner-b".into()); assert_eq!(first.descriptor(), IMAGE); assert_eq!(second.descriptor(), IMAGE); assert_eq!(first.container_name().as_deref(), Some(first_name)); assert_eq!(second.container_name().as_deref(), Some(second_name)); assert_ne!( first.container_name().as_deref(), second.container_name().as_deref() ); assert_eq!( first .labels() .get("wireguard-admin.m8_3_owner") .map(String::as_str), Some("owner-a") ); assert_eq!( second .labels() .get("wireguard-admin.m8_3_owner") .map(String::as_str), Some("owner-b") ); } #[test] fn cleanup_requires_the_exact_run_owner_label_and_a_valid_id() { assert_eq!( owned_resource_id("0123456789abcdef owner-7", "owner-7"), Some("0123456789abcdef".to_owned()) ); assert_eq!( owned_resource_id("0123456789abcdef owner-8", "owner-7"), None ); assert_eq!(owned_resource_id("not-an-id owner-7", "owner-7"), None); } #[test] fn absent_or_uninspectable_partial_start_remains_uncertain() { assert!(matches!( partial_start_status(Ok(None)), PartialStartStatus::Uncertain )); assert!(matches!( partial_start_status(Err("Docker named resource was not found")), PartialStartStatus::Uncertain )); assert!(matches!( partial_start_status(Ok(Some("0123456789abcdef".to_owned()))), PartialStartStatus::ConfirmedOwned(_) )); } #[test] fn fixture_commands_use_busybox_timeout_and_escalate_to_kill() { assert_eq!( bounded_command(&["ip", "-4", "route", "show"]), [ "/bin/busybox", "timeout", "-s", "KILL", "15", "ip", "-4", "route", "show" ] ); } #[test] fn testcontainers_start_failures_map_to_fixed_redacted_categories() { let client_init = TestcontainersError::Client(ClientError::Init( io::Error::other("secret docker client detail").into(), )); let create = TestcontainersError::Client(ClientError::CreateContainer( io::Error::other("secret create detail").into(), )); let start = TestcontainersError::Client(ClientError::StartContainer( io::Error::other("secret start detail").into(), )); let timeout = TestcontainersError::WaitContainer(WaitContainerError::StartupTimeout); let readiness = TestcontainersError::WaitContainer(WaitContainerError::StateUnavailable); let invalid_host = TestcontainersError::Client(ClientError::InvalidDockerHost( "tcp://secret.example.invalid".to_owned(), )); let host_io = TestcontainersError::Io(io::Error::other("private socket path")); let other = TestcontainersError::other("secret-bearing daemon response"); let image_fallback = TestcontainersError::Client(ClientError::PullImage { descriptor: "wireguard-admin-alpine:3.22.7".to_owned(), err: io::Error::other("synthetic create-not-found fallback detail").into(), }); assert_eq!( testcontainers_start_failure_category(&client_init), "Testcontainers Docker client initialization failed" ); assert_eq!( testcontainers_start_failure_category(&create), "Testcontainers container creation failed" ); assert_eq!( testcontainers_start_failure_category(&start), "Testcontainers container start failed" ); assert_eq!( testcontainers_start_failure_category(&timeout), "Testcontainers container readiness timed out" ); assert_eq!( testcontainers_start_failure_category(&readiness), "Testcontainers container readiness failed" ); assert_eq!( testcontainers_start_failure_category(&invalid_host), "Testcontainers Docker host configuration failed" ); assert_eq!( testcontainers_start_failure_category(&host_io), "Testcontainers host I/O failed" ); assert_eq!( testcontainers_start_failure_category(&other), "Testcontainers container startup failed" ); assert_eq!( testcontainers_start_failure_category(&image_fallback), "Testcontainers container creation not-found image fallback failed" ); let categories = [ testcontainers_start_failure_category(&client_init), testcontainers_start_failure_category(&create), testcontainers_start_failure_category(&start), testcontainers_start_failure_category(&timeout), testcontainers_start_failure_category(&readiness), testcontainers_start_failure_category(&invalid_host), testcontainers_start_failure_category(&host_io), testcontainers_start_failure_category(&other), testcontainers_start_failure_category(&image_fallback), ]; assert!( categories .iter() .all(|category| !category.contains("secret")) ); assert!( categories .iter() .all(|category| !category.contains("private")) ); } #[test] fn route_probe_mutates_then_observes_and_removes_ipv4_route() { assert!(ipv4_blackhole_route_present( "blackhole 198.51.100.0/24 proto static\n", "198.51.100.0/24" )); assert!(!ipv4_blackhole_route_present( "192.0.2.0/24 dev lo\n", "198.51.100.0/24" )); assert!(ipv4_route_absent( "192.0.2.0/24 dev lo\n", "198.51.100.0/24" )); assert!(!ipv4_route_absent( "blackhole 198.51.100.0/24 proto static\n", "198.51.100.0/24" )); } }