OXYGENHUB
# 8c0a4e3a8a30 oxygenflow / crates / flow / tests / microvm.rs
2.4 KiB · Rust · last changed OxygenFlow M0: pipeline parser and canonical job graph, receipts, the runner API types, flow-init, the Firecracker tier, flow check, run, rootfs import and runner 1 month ago source blame history raw
1
//! Tier 2 conformance on a real microVM (spec §10.1 and §10.5). Runs when
2
//! `FLOW_TEST_HOME` names a Flow home with `[microvm]` pinned and
3
//! `FLOW_TEST_ROOTFS` the hash of an Alpine rootfs in it; skipped otherwise.
4
5
use std::process::Command;
6
7
#[test]
8
fn a_pipeline_runs_isolated_in_microvms() {
9
    let (Some(home), Some(rootfs)) = (std::env::var_os("FLOW_TEST_HOME"), std::env::var("FLOW_TEST_ROOTFS").ok()) else {
10
        eprintln!("skipped: set FLOW_TEST_HOME and FLOW_TEST_ROOTFS to boot microVMs");
11
        return;
12
    };
13
    let dir = tempfile::tempdir().unwrap();
14
    std::fs::create_dir_all(dir.path().join(".flow")).unwrap();
15
    std::fs::write(
16
        dir.path().join(".flow/pipeline.kdl"),
17
        format!(
18
            r#"schema-version 1
19
sandbox "alpine" {{
20
    rootfs "cas://blob/{rootfs}"
21
    memory-limit "256MiB"
22
}}
23
job "literal" {{
24
    sandbox "alpine"
25
    exec "echo" "a; touch /src/pwned && $(id) | `id`"
26
}}
27
job "isolated" {{
28
    sandbox "alpine"
29
    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"
30
}}
31
job "readonly" {{
32
    sandbox "alpine"
33
    exec "sh" "-c" "touch /etc/x 2>/dev/null && echo ROOTFS-WRITABLE; echo ok > /src/w && cat /src/w; id -u"
34
}}
35
job "fails" {{
36
    sandbox "alpine"
37
    exec "false"
38
}}
39
job "after-failure" {{
40
    sandbox "alpine"
41
    depends-on "fails"
42
    exec "echo" "SHOULD-NOT-RUN"
43
}}
44
"#
45
        ),
46
    )
47
    .unwrap();
48
    let out = Command::new(env!("CARGO_BIN_EXE_flow")).arg("--home").arg(&home).arg("run").current_dir(dir.path()).output().unwrap();
49
    let stdout = String::from_utf8_lossy(&out.stdout);
50
    assert!(!out.status.success(), "a failed job fails the run");
51
    assert!(stdout.contains("   a; touch /src/pwned && $(id) | `id`\n"), "argv is literal: {stdout}");
52
    assert!(!dir.path().join("pwned").exists());
53
    assert!(stdout.contains("   lo\n") && stdout.contains("no-network") && !stdout.contains("   NETWORK\n"), "only loopback: {stdout}");
54
    assert!(stdout.contains("   ok\n") && !stdout.contains("   ROOTFS-WRITABLE\n"), "rootfs read-only, workspace writable: {stdout}");
55
    assert!(!stdout.contains("   0\n"), "the job never runs as root: {stdout}");
56
    assert!(stdout.contains("── fails failed (exit 1)"), "{stdout}");
57
    assert!(stdout.contains("── after-failure skipped: fails failed") && !stdout.contains("   SHOULD-NOT-RUN\n"), "{stdout}");
58
}