//! Tier 2 conformance on a real microVM (spec §10.1 and §10.5). Runs when //! `FLOW_TEST_HOME` names a Flow home with `[microvm]` pinned and //! `FLOW_TEST_ROOTFS` the hash of an Alpine rootfs in it; skipped otherwise. use std::process::Command; #[test] fn a_pipeline_runs_isolated_in_microvms() { let (Some(home), Some(rootfs)) = (std::env::var_os("FLOW_TEST_HOME"), std::env::var("FLOW_TEST_ROOTFS").ok()) else { eprintln!("skipped: set FLOW_TEST_HOME and FLOW_TEST_ROOTFS to boot microVMs"); return; }; let dir = tempfile::tempdir().unwrap(); std::fs::create_dir_all(dir.path().join(".flow")).unwrap(); std::fs::write( dir.path().join(".flow/pipeline.kdl"), format!( r#"schema-version 1 sandbox "alpine" {{ rootfs "cas://blob/{rootfs}" memory-limit "256MiB" }} job "literal" {{ sandbox "alpine" exec "echo" "a; touch /src/pwned && $(id) | `id`" }} job "isolated" {{ sandbox "alpine" exec "sh" "-c" "cat /proc/net/dev | tail -n +3 | cut -d: -f1 | tr -d ' '; wget -q -T 2 -O /dev/null http://1.1.1.1 && echo NETWORK || echo no-network" }} job "readonly" {{ sandbox "alpine" exec "sh" "-c" "touch /etc/x 2>/dev/null && echo ROOTFS-WRITABLE; echo ok > /src/w && cat /src/w; id -u" }} job "fails" {{ sandbox "alpine" exec "false" }} job "after-failure" {{ sandbox "alpine" depends-on "fails" exec "echo" "SHOULD-NOT-RUN" }} "# ), ) .unwrap(); let out = Command::new(env!("CARGO_BIN_EXE_flow")).arg("--home").arg(&home).arg("run").current_dir(dir.path()).output().unwrap(); let stdout = String::from_utf8_lossy(&out.stdout); assert!(!out.status.success(), "a failed job fails the run"); assert!(stdout.contains(" a; touch /src/pwned && $(id) | `id`\n"), "argv is literal: {stdout}"); assert!(!dir.path().join("pwned").exists()); assert!(stdout.contains(" lo\n") && stdout.contains("no-network") && !stdout.contains(" NETWORK\n"), "only loopback: {stdout}"); assert!(stdout.contains(" ok\n") && !stdout.contains(" ROOTFS-WRITABLE\n"), "rootfs read-only, workspace writable: {stdout}"); assert!(!stdout.contains(" 0\n"), "the job never runs as root: {stdout}"); assert!(stdout.contains("── fails failed (exit 1)"), "{stdout}"); assert!(stdout.contains("── after-failure skipped: fails failed") && !stdout.contains(" SHOULD-NOT-RUN\n"), "{stdout}"); }