//! 共享执行器回归使用 Pod 内的真实子进程,不依赖线上服务。 use super::super::super::Directory; use dever::compiler::project::dependency::library::build::process; use std::{fs, path::Path, process::Command}; fn child_gone(path: &Path) { let pid = fs::read_to_string(path).unwrap(); assert!( !Path::new("/proc").join(pid.trim()).exists(), "child {pid} was not reaped" ); } #[test] fn lib_build_output_overflow_drains_pipes_and_reaps_its_group() { let directory = Directory::new(); let mut command = Command::new("/bin/sh"); command.current_dir(&directory.0).args(["-c", "echo $$ > parent; /bin/sleep 300 & echo $! > child; /usr/bin/head -c 33554432 /dev/zero; wait"]); let error = process::run(command, &directory.0).unwrap_err(); assert!(error.contains("16 MiB"), "{error}"); child_gone(&directory.0.join("parent")); child_gone(&directory.0.join("child")); assert!( fs::read_dir(&directory.0) .unwrap() .all(|entry| entry.unwrap().metadata().unwrap().len() < 32) ); } #[test] fn lib_build_success_reaps_descendants_holding_output_open() { let directory = Directory::new(); let mut command = Command::new("/bin/sh"); command .current_dir(&directory.0) .args(["-c", "/bin/sleep 300 & echo $! > child; exit 0"]); assert!(process::run(command, &directory.0).unwrap().success()); child_gone(&directory.0.join("child")); } #[test] fn lib_build_scratch_budget_kills_and_reaps_before_returning() { let directory = Directory::new(); let mut command = Command::new("/bin/sh"); command.current_dir(&directory.0).args(["-c", "echo $$ > parent; /bin/sleep 300 & echo $! > child; /usr/bin/truncate -s 536870913 excess; wait"]); let error = process::run(command, &directory.0).unwrap_err(); assert!(error.contains("512 MiB"), "{error}"); child_gone(&directory.0.join("parent")); child_gone(&directory.0.join("child")); } #[test] fn lib_build_probe_distinguishes_unloadable_exit_and_signal_from_budget_errors() { let directory = Directory::new(); for script in ["exit 10", "ulimit -c 0; kill -SEGV $$"] { let mut command = Command::new("/bin/sh"); command.args(["-c", script]); let status = process::status(command, &directory.0).unwrap(); assert!(!status.success()); } let mut command = Command::new("/bin/sh"); command.args(["-c", "exit 10"]); assert!( process::run(command, &directory.0) .unwrap_err() .contains("Lib build failed") ); } #[test] fn lib_build_script_header_cannot_select_host_tools_or_inject_arguments() { let fixture = super::Fixture::new(); for header in [ "#!/bin/bash", "#!/usr/bin/env node --eval unsafe", "#!/tmp/untrusted", ] { let mut stage = dever::compiler::project::dependency::library::build::stage::Stage::new( &fixture.signed, ) .unwrap(); stage .write( "input/rootfs/usr/bin/script", format!("{header}\nexit 0\n").into_bytes(), true, ) .unwrap(); let tools = ["rootfs/usr/bin/script".to_owned()].into_iter().collect(); assert!( stage .prepare_tools(&tools) .unwrap_err() .contains("signed interpreter") ); } }