test: add --mode vm, the autopkgtest escape hatch
VM virtualization is the one backend pkh does not reimplement: the qemu runner carries the serial-console protocol, image overlay management and reboot support, and it is the only one satisfying isolation-machine/needs-reboot tests. pkh execs the installed autopkgtest with its qemu runner (--image required, --cpus/--ram-size passthrough) in its own process group so Ctrl+C reaches the testbed cleanup, parses the --summary file back into the pkh report model and maps autopkgtest's exit classes onto the 0/1/2 contract (12/14/16/20 are runtime errors; the raw code rides the JSON report). Pockets, PPAs and injection are refused in vm mode for now: they would need the keyring setup inside the image.
This commit is contained in:
+62
-13
@@ -17,6 +17,7 @@ pub mod expand;
|
||||
pub mod options;
|
||||
pub mod outcome;
|
||||
pub(crate) mod runner;
|
||||
pub(crate) mod vm;
|
||||
|
||||
pub use options::{TestMode, TestOptions};
|
||||
pub use outcome::TestRunReport;
|
||||
@@ -117,6 +118,38 @@ pub async fn run(opts: TestOptions<'_>) -> Result<TestRunReport, Box<dyn std::er
|
||||
});
|
||||
|
||||
let mode = opts.mode.unwrap_or_default();
|
||||
|
||||
// The vm escape hatch goes through the installed autopkgtest; it
|
||||
// owns the artifacts directory (passed as -o, which must not exist
|
||||
// yet), so the path is only computed, not created.
|
||||
if mode == TestMode::Vm {
|
||||
let run_dir = run_dir_path(&package, &series, &arch);
|
||||
let (outcomes, raw_code) = vm::run_vm(
|
||||
&tree,
|
||||
&binaries.paths,
|
||||
&tests_control.tests,
|
||||
&run_dir,
|
||||
opts.image.as_deref(),
|
||||
&opts,
|
||||
view,
|
||||
)?;
|
||||
return finish(
|
||||
TestRunReport {
|
||||
package,
|
||||
version,
|
||||
series,
|
||||
arch,
|
||||
mode: mode.label().to_string(),
|
||||
binaries: binaries.paths,
|
||||
binaries_note: binaries.note,
|
||||
outcomes,
|
||||
artifacts_dir: run_dir,
|
||||
raw_exit_code: Some(raw_code),
|
||||
},
|
||||
view,
|
||||
);
|
||||
}
|
||||
|
||||
if mode == TestMode::Local {
|
||||
log::warn!(
|
||||
"Tests run directly on the current context: they install packages \
|
||||
@@ -217,22 +250,32 @@ pub async fn run(opts: TestOptions<'_>) -> Result<TestRunReport, Box<dyn std::er
|
||||
drop_shell(&bed, &staged_tree, &env, view)?;
|
||||
}
|
||||
|
||||
let report = TestRunReport {
|
||||
package,
|
||||
version,
|
||||
series,
|
||||
arch,
|
||||
mode: mode.label().to_string(),
|
||||
binaries: binaries.paths,
|
||||
binaries_note: binaries.note,
|
||||
outcomes,
|
||||
artifacts_dir: run_dir.clone(),
|
||||
raw_exit_code: None,
|
||||
};
|
||||
finish(
|
||||
TestRunReport {
|
||||
package,
|
||||
version,
|
||||
series,
|
||||
arch,
|
||||
mode: mode.label().to_string(),
|
||||
binaries: binaries.paths,
|
||||
binaries_note: binaries.note,
|
||||
outcomes,
|
||||
artifacts_dir: run_dir,
|
||||
raw_exit_code: None,
|
||||
},
|
||||
view,
|
||||
)
|
||||
}
|
||||
|
||||
/// Announce the outcome on the view and hand the report back.
|
||||
fn finish(
|
||||
report: TestRunReport,
|
||||
view: &dyn BuildView,
|
||||
) -> Result<TestRunReport, Box<dyn std::error::Error>> {
|
||||
if report.has_failures() {
|
||||
view.finish_failure();
|
||||
} else {
|
||||
view.finish_success(&[run_dir]);
|
||||
view.finish_success(std::slice::from_ref(&report.artifacts_dir));
|
||||
}
|
||||
Ok(report)
|
||||
}
|
||||
@@ -535,6 +578,12 @@ fn create_run_dir(package: &str, series: &str, arch: &str) -> std::io::Result<Pa
|
||||
Ok(dir)
|
||||
}
|
||||
|
||||
/// The run directory path for an identity, without creating it.
|
||||
fn run_dir_path(package: &str, series: &str, arch: &str) -> PathBuf {
|
||||
let timestamp = chrono::Utc::now().format("%Y%m%dT%H%M%S");
|
||||
runs_root().join(format!("{package}-{series}-{arch}-{timestamp}"))
|
||||
}
|
||||
|
||||
/// The test-run artifacts root: `$PKH_TESTS_DIR`, defaulting to
|
||||
/// `/var/tmp/pkh/tests`.
|
||||
pub fn runs_root() -> PathBuf {
|
||||
|
||||
Reference in New Issue
Block a user