atagen

roundabout

public
Star0
Watch0
9117440444eb609ecea8476b5a62b03917077591
1
ssh://brewery@pls.lobotomise.me:33/atagen/roundabout.git
tests/systemd.rsRust147 lines4.9 KB
1
#![cfg(target_os = "linux")]
2
3
use std::{
4
    fs,
5
    io::Write,
6
    os::fd::{AsRawFd, FromRawFd, OwnedFd},
7
    os::unix::process::CommandExt,
8
    process::{Child, Command, Stdio},
9
    time::Duration,
10
};
11
12
fn gated_command(program: &str, arguments: &[&str]) -> (Child, OwnedFd) {
13
    let mut descriptors = [-1; 2];
14
    assert_eq!(
15
        unsafe { libc::pipe2(descriptors.as_mut_ptr(), libc::O_CLOEXEC) },
16
        0
17
    );
18
    let read = unsafe { OwnedFd::from_raw_fd(descriptors[0]) };
19
    let write = unsafe { OwnedFd::from_raw_fd(descriptors[1]) };
20
    let descriptor = read.as_raw_fd();
21
    let mut command = Command::new(env!("CARGO_BIN_EXE_roundabout"));
22
    command
23
        .args(["__roundabout_exec", &descriptor.to_string(), program])
24
        .args(arguments)
25
        .stdout(Stdio::piped())
26
        .stderr(Stdio::piped());
27
    unsafe {
28
        command.pre_exec(move || {
29
            if libc::fcntl(descriptor, libc::F_SETFD, 0) == -1 {
30
                return Err(std::io::Error::last_os_error());
31
            }
32
            Ok(())
33
        });
34
    }
35
    let child = command.spawn().unwrap();
36
    drop(read);
37
    (child, write)
38
}
39
40
#[test]
41
fn command_waits_for_scope_setup() {
42
    let (mut child, gate) = gated_command(env!("CARGO_BIN_EXE_roundabout"), &["--version"]);
43
    let mut output = libc::pollfd {
44
        fd: child.stdout.as_ref().unwrap().as_raw_fd(),
45
        events: libc::POLLIN,
46
        revents: 0,
47
    };
48
    assert_eq!(unsafe { libc::poll(&raw mut output, 1, 100) }, 0);
49
    assert!(child.try_wait().unwrap().is_none());
50
    fs::File::from(gate).write_all(b"1").unwrap();
51
    let output = child.wait_with_output().unwrap();
52
    assert!(output.status.success());
53
    assert!(
54
        String::from_utf8(output.stdout)
55
            .unwrap()
56
            .starts_with("roundabout ")
57
    );
58
}
59
60
#[test]
61
fn failed_scope_setup_never_executes_the_command() {
62
    let (child, gate) = gated_command(env!("CARGO_BIN_EXE_roundabout"), &["--version"]);
63
    drop(gate);
64
    let output = child.wait_with_output().unwrap();
65
    assert_eq!(output.status.code(), Some(1));
66
    assert!(output.stdout.is_empty());
67
}
68
69
#[test]
70
fn missing_commands_keep_exit_127() {
71
    let (child, gate) = gated_command("/roundabout-missing-command", &[]);
72
    fs::File::from(gate).write_all(b"1").unwrap();
73
    assert_eq!(child.wait_with_output().unwrap().status.code(), Some(127));
74
}
75
76
#[test]
77
#[ignore = "requires a systemd user manager and cgroup v2"]
78
fn transient_scope_enforces_the_grant() {
79
    if std::env::var_os("ROUNDABOUT_SCOPE_PROBE").is_some() {
80
        let membership = fs::read_to_string("/proc/self/cgroup").unwrap();
81
        let path = membership
82
            .lines()
83
            .find_map(|line| line.strip_prefix("0::"))
84
            .unwrap();
85
        assert!(path.contains("roundabout-"));
86
        let cgroup = std::path::Path::new("/sys/fs/cgroup").join(path.trim_start_matches('/'));
87
        let cpu = fs::read_to_string(cgroup.join("cpu.max")).unwrap();
88
        let limits: Vec<_> = cpu.split_whitespace().collect();
89
        assert_eq!(limits[0], limits[1]);
90
        assert_eq!(
91
            fs::read_to_string(cgroup.join("memory.max"))
92
                .unwrap()
93
                .trim(),
94
            (80 * 1024 * 1024).to_string()
95
        );
96
        assert_eq!(
97
            fs::read_to_string(cgroup.join("memory.high"))
98
                .unwrap()
99
                .trim(),
100
            (64 * 1024 * 1024).to_string()
101
        );
102
        assert_eq!(
103
            fs::read_to_string(cgroup.join("memory.oom.group"))
104
                .unwrap()
105
                .trim(),
106
            "1"
107
        );
108
        assert_eq!(std::env::var("CARGO_BUILD_JOBS").unwrap(), "1");
109
        assert!(std::env::var("ROUNDABOUT_LEASE").is_ok());
110
        return;
111
    }
112
    let directory =
113
        std::env::temp_dir().join(format!("roundabout-scope-test-{}", std::process::id()));
114
    fs::create_dir_all(&directory).unwrap();
115
    let config = directory.join("config.toml");
116
    fs::write(
117
        &config,
118
        "[overrun]\nram = \"throttle\"\n[queue]\nlinger = \"0s\"\n",
119
    )
120
    .unwrap();
121
    let output = Command::new(env!("CARGO_BIN_EXE_roundabout"))
122
        .args([
123
            "run", "--ram", "64M", "--disk", "0", "--cores", "1", "--time", "5s", "--exact", "--",
124
        ])
125
        .arg(std::env::current_exe().unwrap())
126
        .args([
127
            "--exact",
128
            "transient_scope_enforces_the_grant",
129
            "--ignored",
130
            "--nocapture",
131
        ])
132
        .current_dir(&directory)
133
        .env("ROUNDABOUT_CONFIG", &config)
134
        .env("ROUNDABOUT_RUNTIME_DIR", directory.join("runtime"))
135
        .env("ROUNDABOUT_STATE_DIR", directory.join("state"))
136
        .env("ROUNDABOUT_SCOPE_PROBE", "1")
137
        .output()
138
        .unwrap();
139
    std::thread::sleep(Duration::from_secs(2));
140
    fs::remove_dir_all(directory).unwrap();
141
    assert!(
142
        output.status.success(),
143
        "{}\n{}",
144
        String::from_utf8_lossy(&output.stdout),
145
        String::from_utf8_lossy(&output.stderr)
146
    );
147
}