Skip to content

Commit e2ba5a2

Browse files
authored
Merge branch 'main' into misc_fixes
2 parents 7ffc167 + 0fd78cf commit e2ba5a2

File tree

10 files changed

+140
-40
lines changed

10 files changed

+140
-40
lines changed

.buildkite/pipeline_perf.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@
6060
"tests": "integration_tests/performance/test_memory_overhead.py integration_tests/performance/test_boottime.py::test_boottime",
6161
"devtool_opts": "-c 1-10 -m 0",
6262
},
63+
"jailer": {
64+
"label": "⛓️ jailer",
65+
"tests": "integration_tests/performance/test_jailer.py",
66+
"devtool_opts": "-c 1-10 -m 0",
67+
},
6368
}
6469

6570
REVISION_A = os.environ.get("REVISION_A")

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ and this project adheres to
2222
- [#5165](https://github.com/firecracker-microvm/firecracker/pull/5165): Changed
2323
Firecracker snapshot feature from developer preview to generally available.
2424
Incremental snapshots remain in developer preview.
25+
- [#5282](https://github.com/firecracker-microvm/firecracker/pull/5282): Updated
26+
jailer to no longer require the executable file name to contain `firecracker`.
2527

2628
### Deprecated
2729

src/jailer/src/env.rs

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -301,10 +301,6 @@ impl Env {
301301
.unwrap()
302302
.to_string();
303303

304-
if !exec_file_name.contains("firecracker") {
305-
return Err(JailerError::ExecFileName(exec_file_name));
306-
}
307-
308304
Ok((exec_file_path, exec_file_name))
309305
}
310306

@@ -1048,17 +1044,6 @@ mod tests {
10481044
"/tmp/firecracker_test_dir is not a file"
10491045
);
10501046

1051-
// Error case 3: Filename without "firecracker"
1052-
File::create("/tmp/firecracker_test_dir/foobarbaz").unwrap();
1053-
assert_eq!(
1054-
format!(
1055-
"{}",
1056-
Env::validate_exec_file("/tmp/firecracker_test_dir/foobarbaz").unwrap_err()
1057-
),
1058-
"Invalid filename. The filename of `--exec-file` option must contain \"firecracker\": \
1059-
foobarbaz"
1060-
);
1061-
std::fs::remove_file("/tmp/firecracker_test_dir/foobarbaz").unwrap();
10621047
std::fs::remove_dir_all("/tmp/firecracker_test_dir").unwrap();
10631048
}
10641049

src/jailer/src/main.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,6 @@ pub enum JailerError {
7373
Dup2(io::Error),
7474
#[error("Failed to exec into Firecracker: {0}")]
7575
Exec(io::Error),
76-
#[error(
77-
"Invalid filename. The filename of `--exec-file` option must contain \"firecracker\": {0}"
78-
)]
79-
ExecFileName(String),
8076
#[error("{}", format!("Failed to extract filename from path {:?}", .0).replace('\"', ""))]
8177
ExtractFileName(PathBuf),
8278
#[error("{}", format!("Failed to open file {:?}: {}", .0, .1).replace('\"', ""))]
@@ -351,8 +347,7 @@ fn main_exec() -> Result<(), JailerError> {
351347
fs::create_dir_all(env.chroot_dir())
352348
.map_err(|err| JailerError::CreateDir(env.chroot_dir().to_owned(), err))?;
353349
env.run()
354-
})
355-
.unwrap_or_else(|err| panic!("Jailer error: {}", err));
350+
})?;
356351
Ok(())
357352
}
358353

tests/conftest.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,17 @@ def msr_reader_bin(test_fc_session_root_path):
274274
yield msr_reader_bin_path
275275

276276

277+
@pytest.fixture(scope="session")
278+
def jailer_time_bin(test_fc_session_root_path):
279+
"""Build a binary that fakes fc"""
280+
jailer_time_bin_path = os.path.join(test_fc_session_root_path, "jailer_time")
281+
build_tools.gcc_compile(
282+
"host_tools/jailer_time.c",
283+
jailer_time_bin_path,
284+
)
285+
yield jailer_time_bin_path
286+
287+
277288
@pytest.fixture
278289
def bin_seccomp_paths():
279290
"""Build jailers and jailed binaries to test seccomp.

tests/framework/microvm.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1206,13 +1206,20 @@ def build_n_from_snapshot(
12061206
uffd_handler_name=None,
12071207
incremental=False,
12081208
use_snapshot_editor=True,
1209+
no_netns_reuse=False,
12091210
):
12101211
"""A generator of `n` microvms restored, either all restored from the same given snapshot
12111212
(incremental=False), or created by taking successive snapshots of restored VMs
12121213
"""
12131214
last_snapshot = None
12141215
for _ in range(nr_vms):
1215-
microvm = self.build()
1216+
microvm = self.build(
1217+
**(
1218+
{"netns": net_tools.NetNs(str(uuid.uuid4()))}
1219+
if no_netns_reuse
1220+
else {}
1221+
)
1222+
)
12161223
microvm.spawn()
12171224

12181225
snapshot_copy = microvm.restore_from_snapshot(

tests/host_tools/jailer_time.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
// This is used by `performance/test_jailer.py`
5+
6+
#include <stdio.h>
7+
#include <time.h>
8+
9+
int main(int argc, char** argv) {
10+
// print current time in us
11+
struct timespec now = {0};
12+
clock_gettime(CLOCK_MONOTONIC, &now);
13+
unsigned long long current_ns = (unsigned long long)now.tv_sec * 1000000000 + (unsigned long long)now.tv_nsec;
14+
unsigned long long current_us = current_ns / 1000;
15+
printf("%llu\n", current_us);
16+
17+
// print the --start-time-us value
18+
printf("%s", argv[4]);
19+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
# SPDX-License-Identifier: Apache-2.0
3+
"""Performance benchmark for the jailer."""
4+
5+
import os
6+
import shutil
7+
from concurrent.futures import ProcessPoolExecutor
8+
9+
import pytest
10+
11+
from framework import utils
12+
from framework.jailer import DEFAULT_CHROOT_PATH, JailerContext
13+
from framework.properties import global_props
14+
15+
16+
def setup_bind_mounts(tmp_path, n):
17+
"""
18+
Create bind mount points. The exact location of them
19+
does not matter, they just need to exist.
20+
"""
21+
mounts_paths = tmp_path / "mounts"
22+
os.makedirs(mounts_paths)
23+
for m in range(n):
24+
mount_path = f"{mounts_paths}/mount{m}"
25+
os.makedirs(mount_path)
26+
utils.check_output(f"mount --bind {mount_path} {mount_path}")
27+
28+
29+
def clean_up_mounts(tmp_path):
30+
"""Cleanup mounts and jailer dirs"""
31+
mounts_paths = tmp_path / "mounts"
32+
for d in os.listdir(mounts_paths):
33+
utils.check_output(f"umount {mounts_paths}/{d}")
34+
35+
36+
@pytest.mark.nonci
37+
@pytest.mark.parametrize("parallel", [1, 5, 10])
38+
@pytest.mark.parametrize("mounts", [0, 100, 300, 500])
39+
def test_jailer_startup(
40+
jailer_time_bin, tmp_path, microvm_factory, parallel, mounts, metrics
41+
):
42+
"""
43+
Test the overhead of jailer startup without and with bind mounts
44+
with different parallelism options.
45+
"""
46+
47+
jailer_binary = microvm_factory.jailer_binary_path
48+
49+
setup_bind_mounts(tmp_path, mounts)
50+
51+
metrics.set_dimensions(
52+
{
53+
"instance": global_props.instance,
54+
"cpu_model": global_props.cpu_model,
55+
"performance_test": "test_jailer_startup",
56+
"parallel": str(parallel),
57+
"mounts": str(mounts),
58+
}
59+
)
60+
61+
cmds = []
62+
for i in range(500):
63+
jailer = JailerContext(
64+
jailer_id=f"fakefc{i}",
65+
exec_file=jailer_time_bin,
66+
# Don't deamonize to get the stdout
67+
daemonize=False,
68+
)
69+
jailer.setup()
70+
71+
cmd = [str(jailer_binary), *jailer.construct_param_list()]
72+
cmds.append(cmd)
73+
74+
with ProcessPoolExecutor(max_workers=parallel) as executor:
75+
# Submit all commands and get results
76+
results = executor.map(utils.check_output, cmds)
77+
78+
# Get results as they complete
79+
for result in results:
80+
end_time, start_time = result.stdout.split()
81+
metrics.put_metric(
82+
"startup",
83+
int(end_time) - int(start_time),
84+
unit="Microseconds",
85+
)
86+
87+
clean_up_mounts(tmp_path)
88+
shutil.rmtree(DEFAULT_CHROOT_PATH)

tests/integration_tests/performance/test_snapshot.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,9 @@ def test_restore_latency(
122122

123123
snapshot = vm.snapshot_full()
124124
vm.kill()
125-
for microvm in microvm_factory.build_n_from_snapshot(snapshot, ITERATIONS):
125+
for microvm in microvm_factory.build_n_from_snapshot(
126+
snapshot, ITERATIONS, no_netns_reuse=True
127+
):
126128
value = 0
127129
# Parse all metric data points in search of load_snapshot time.
128130
microvm.flush_metrics()

tests/integration_tests/security/test_jail.py

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def test_empty_jailer_id(uvm_plain):
6969
# we can set an empty ID.
7070
with pytest.raises(
7171
ChildProcessError,
72-
match=r"Jailer error: Invalid instance ID: Invalid len \(0\); the length must be between 1 and 64",
72+
match=r"Invalid instance ID: Invalid len \(0\); the length must be between 1 and 64",
7373
):
7474
test_microvm.spawn()
7575

@@ -88,7 +88,7 @@ def test_exec_file_not_exist(uvm_plain, tmp_path):
8888

8989
with pytest.raises(
9090
Exception,
91-
match=rf"Jailer error: Failed to canonicalize path {pseudo_exec_file_path}:"
91+
match=rf"Failed to canonicalize path {pseudo_exec_file_path}:"
9292
rf" No such file or directory \(os error 2\)",
9393
):
9494
test_microvm.spawn()
@@ -102,21 +102,7 @@ def test_exec_file_not_exist(uvm_plain, tmp_path):
102102

103103
with pytest.raises(
104104
Exception,
105-
match=rf"Jailer error: {pseudo_exec_dir_path} is not a file",
106-
):
107-
test_microvm.spawn()
108-
109-
# Error case 3: Filename without "firecracker"
110-
pseudo_exec_file_path = tmp_path / "foobarbaz"
111-
pseudo_exec_file_path.touch()
112-
fc_dir = Path("/srv/jailer") / pseudo_exec_file_path.name / test_microvm.id
113-
fc_dir.mkdir(parents=True, exist_ok=True)
114-
test_microvm.jailer.exec_file = pseudo_exec_file_path
115-
116-
with pytest.raises(
117-
Exception,
118-
match=r"Jailer error: Invalid filename. The filename of `--exec-file` option"
119-
r' must contain "firecracker": foobarbaz',
105+
match=rf"{pseudo_exec_dir_path} is not a file",
120106
):
121107
test_microvm.spawn()
122108

0 commit comments

Comments
 (0)