Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .github/workflows/pr_tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: PR Tests

on:
pull_request:

env:
CARGO_TERM_COLOR: always

jobs:
tests:
name: Tests
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Run tests with devtool
env:
PYTEST_ADDOPTS: "-n 2"
run: |
./tools/devtool -y test -- integration_tests/functional/
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ test_results/*
/resources/linux
/resources/x86_64
/resources/aarch64
.env
2 changes: 2 additions & 0 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
gcloud 534.0.0
rust 1.85.0
12 changes: 12 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
-include .env

.PHONY: build
build:
./scripts/build.sh

.PHONY: upload
upload:
./scripts/upload.sh $(GCP_PROJECT_ID)

.PHONY: build-and-upload
make build-and-upload: build upload
4 changes: 4 additions & 0 deletions resources/seccomp/aarch64-unknown-linux-musl.json
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,10 @@
"syscall": "madvise",
"comment": "Used by the VirtIO balloon device and by musl for some customer workloads. It is also used by aws-lc during random number generation. They setup a memory page that mark with MADV_WIPEONFORK to be able to detect forks. They also call it with -1 to see if madvise is supported in certain platforms."
},
{
"syscall": "mincore",
"comment": "Used by get_memory_dirty_bitmap to check if memory pages are resident"
},
{
"syscall": "mmap",
"comment": "Used by the VirtIO balloon device",
Expand Down
10 changes: 7 additions & 3 deletions resources/seccomp/x86_64-unknown-linux-musl.json
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,10 @@
"syscall": "madvise",
"comment": "Used by the VirtIO balloon device and by musl for some customer workloads. It is also used by aws-lc during random number generation. They setup a memory page that mark with MADV_WIPEONFORK to be able to detect forks. They also call it with -1 to see if madvise is supported in certain platforms."
},
{
"syscall": "mincore",
"comment": "Used by get_memory_dirty_bitmap to check if memory pages are resident"
},
{
"syscall": "mmap",
"comment": "Used by the VirtIO balloon device",
Expand Down Expand Up @@ -524,8 +528,8 @@
"comment": "sigaltstack is used by Rust stdlib to remove alternative signal stack during thread teardown."
},
{
"syscall": "getrandom",
"comment": "getrandom is used by `HttpServer` to reinialize `HashMap` after moving to the API thread"
"syscall": "getrandom",
"comment": "getrandom is used by `HttpServer` to reinialize `HashMap` after moving to the API thread"
},
{
"syscall": "accept4",
Expand Down Expand Up @@ -1152,4 +1156,4 @@
}
]
}
}
}
17 changes: 17 additions & 0 deletions scripts/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/bin/bash

set -euo pipefail

# The format will be: v<major>.<minor>.<patch>_g<commit_hash> — e.g. v1.7.2_g8bb88311
# Extract full version from src/firecracker/swagger/firecracker.yaml
FC_VERSION=$(awk '/^info:/{flag=1} flag && /^ version:/{print $2; exit}' src/firecracker/swagger/firecracker.yaml)
commit_hash=$(git rev-parse --short HEAD)
version_name="v${FC_VERSION}_g${commit_hash}"
echo "Version name: $version_name"

echo "Starting to build Firecracker version: $version_name"
tools/devtool -y build --release

mkdir -p "./build/fc/${version_name}"
cp ./build/cargo_target/x86_64-unknown-linux-musl/release/firecracker "./build/fc/${version_name}/firecracker"
echo "Finished building Firecracker version: $version_name and copied to ./build/fc/${version_name}/firecracker"
13 changes: 13 additions & 0 deletions scripts/upload.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/bash

set -euo pipefail

GCP_PROJECT_ID=$1

gsutil -h "Cache-Control:no-cache, max-age=0" cp -r "build/fc/*" "gs://${GCP_PROJECT_ID}-fc-versions"
if [ "$GCP_PROJECT_ID" == "e2b-prod" ]; then
# Upload kernel to GCP public builds bucket
gsutil -h "Cache-Control:no-cache, max-age=0" cp -r "build/fc/*" "gs://${GCP_PROJECT_ID}-public-builds/firecrackers/"
fi

rm -rf build/fc/*
1 change: 1 addition & 0 deletions src/cpu-template-helper/src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ pub fn build_microvm_from_config(
state: VmState::NotStarted,
vmm_version: CPU_TEMPLATE_HELPER_VERSION.to_string(),
app_name: "cpu-template-helper".to_string(),
memory_regions: None,
};
let mut vm_resources =
VmResources::from_json(&config, &instance_info, HTTP_MAX_PAYLOAD_SIZE, None)
Expand Down
4 changes: 2 additions & 2 deletions src/firecracker/src/api_server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ mod tests {
Box::new(VmmAction::CreateSnapshot(CreateSnapshotParams {
snapshot_type: SnapshotType::Diff,
snapshot_path: PathBuf::new(),
mem_file_path: PathBuf::new(),
mem_file_path: Some(PathBuf::new()),
})),
start_time_us,
);
Expand All @@ -288,7 +288,7 @@ mod tests {
Box::new(VmmAction::CreateSnapshot(CreateSnapshotParams {
snapshot_type: SnapshotType::Diff,
snapshot_path: PathBuf::new(),
mem_file_path: PathBuf::new(),
mem_file_path: Some(PathBuf::new()),
})),
start_time_us,
);
Expand Down
50 changes: 50 additions & 0 deletions src/firecracker/src/api_server/parsed_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use super::request::logger::parse_put_logger;
use super::request::machine_configuration::{
parse_get_machine_config, parse_patch_machine_config, parse_put_machine_config,
};
use super::request::memory::{parse_get_memory, parse_get_memory_mappings};
use super::request::metrics::parse_put_metrics;
use super::request::mmds::{parse_get_mmds, parse_patch_mmds, parse_put_mmds};
use super::request::net::{parse_patch_net, parse_put_net};
Expand Down Expand Up @@ -82,6 +83,14 @@ impl TryFrom<&Request> for ParsedRequest {
Ok(ParsedRequest::new_sync(VmmAction::GetFullVmConfig))
}
(Method::Get, "machine-config", None) => parse_get_machine_config(),
(Method::Get, "memory", None) => match path_tokens.next() {
Some("mappings") => parse_get_memory_mappings(),
None => parse_get_memory(),
_ => Err(RequestError::InvalidPathMethod(
request_uri.to_string(),
Method::Get,
)),
},
(Method::Get, "mmds", None) => parse_get_mmds(),
(Method::Get, _, Some(_)) => method_to_error(Method::Get),
(Method::Put, "actions", Some(body)) => parse_put_actions(body),
Expand Down Expand Up @@ -172,6 +181,8 @@ impl ParsedRequest {
}
VmmData::BalloonStats(stats) => Self::success_response_with_data(stats),
VmmData::InstanceInformation(info) => Self::success_response_with_data(info),
VmmData::MemoryMappings(mappings) => Self::success_response_with_data(mappings),
VmmData::Memory(memory) => Self::success_response_with_data(memory),
VmmData::VmmVersion(version) => Self::success_response_with_data(
&serde_json::json!({ "firecracker_version": version.as_str() }),
),
Expand Down Expand Up @@ -568,6 +579,12 @@ pub mod tests {
VmmData::InstanceInformation(info) => {
http_response(&serde_json::to_string(info).unwrap(), 200)
}
VmmData::MemoryMappings(mappings) => {
http_response(&serde_json::to_string(mappings).unwrap(), 200)
}
VmmData::Memory(memory) => {
http_response(&serde_json::to_string(memory).unwrap(), 200)
}
VmmData::VmmVersion(version) => http_response(
&serde_json::json!({ "firecracker_version": version.as_str() }).to_string(),
200,
Expand All @@ -589,6 +606,15 @@ pub mod tests {
verify_ok_response_with(VmmData::MachineConfiguration(MachineConfig::default()));
verify_ok_response_with(VmmData::MmdsValue(serde_json::from_str("{}").unwrap()));
verify_ok_response_with(VmmData::InstanceInformation(InstanceInfo::default()));
verify_ok_response_with(VmmData::MemoryMappings(
vmm::vmm_config::instance_info::MemoryMappingsResponse { mappings: vec![] },
));
verify_ok_response_with(VmmData::Memory(
vmm::vmm_config::instance_info::MemoryResponse {
resident: vec![],
empty: vec![],
},
));
verify_ok_response_with(VmmData::VmmVersion(String::default()));

// Error.
Expand Down Expand Up @@ -662,6 +688,30 @@ pub mod tests {
ParsedRequest::try_from(&req).unwrap();
}

#[test]
fn test_try_from_get_memory_mappings() {
let (mut sender, receiver) = UnixStream::pair().unwrap();
let mut connection = HttpConnection::new(receiver);
sender
.write_all(http_request("GET", "/memory/mappings", None).as_bytes())
.unwrap();
connection.try_read().unwrap();
let req = connection.pop_parsed_request().unwrap();
ParsedRequest::try_from(&req).unwrap();
}

#[test]
fn test_try_from_get_memory() {
let (mut sender, receiver) = UnixStream::pair().unwrap();
let mut connection = HttpConnection::new(receiver);
sender
.write_all(http_request("GET", "/memory", None).as_bytes())
.unwrap();
connection.try_read().unwrap();
let req = connection.pop_parsed_request().unwrap();
ParsedRequest::try_from(&req).unwrap();
}

#[test]
fn test_try_from_get_version() {
let (mut sender, receiver) = UnixStream::pair().unwrap();
Expand Down
39 changes: 39 additions & 0 deletions src/firecracker/src/api_server/request/memory.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

use vmm::logger::{IncMetric, METRICS};
use vmm::rpc_interface::VmmAction;

use super::super::parsed_request::{ParsedRequest, RequestError};

pub(crate) fn parse_get_memory_mappings() -> Result<ParsedRequest, RequestError> {
METRICS.get_api_requests.instance_info_count.inc();
Ok(ParsedRequest::new_sync(VmmAction::GetMemoryMappings))
}

pub(crate) fn parse_get_memory() -> Result<ParsedRequest, RequestError> {
METRICS.get_api_requests.instance_info_count.inc();
Ok(ParsedRequest::new_sync(VmmAction::GetMemory))
}

#[cfg(test)]
mod tests {
use super::*;
use crate::api_server::parsed_request::RequestAction;

#[test]
fn test_parse_get_memory_mappings_request() {
match parse_get_memory_mappings().unwrap().into_parts() {
(RequestAction::Sync(action), _) if *action == VmmAction::GetMemoryMappings => {}
_ => panic!("Test failed."),
}
}

#[test]
fn test_parse_get_memory_request() {
match parse_get_memory().unwrap().into_parts() {
(RequestAction::Sync(action), _) if *action == VmmAction::GetMemory => {}
_ => panic!("Test failed."),
}
}
}
1 change: 1 addition & 0 deletions src/firecracker/src/api_server/request/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub mod entropy;
pub mod instance_info;
pub mod logger;
pub mod machine_configuration;
pub mod memory;
pub mod metrics;
pub mod mmds;
pub mod net;
Expand Down
4 changes: 2 additions & 2 deletions src/firecracker/src/api_server/request/snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ mod tests {
let expected_config = CreateSnapshotParams {
snapshot_type: SnapshotType::Diff,
snapshot_path: PathBuf::from("foo"),
mem_file_path: PathBuf::from("bar"),
mem_file_path: Some(PathBuf::from("bar")),
};
assert_eq!(
vmm_action_from_request(parse_put_snapshot(&Body::new(body), Some("create")).unwrap()),
Expand All @@ -154,7 +154,7 @@ mod tests {
let expected_config = CreateSnapshotParams {
snapshot_type: SnapshotType::Full,
snapshot_path: PathBuf::from("foo"),
mem_file_path: PathBuf::from("bar"),
mem_file_path: Some(PathBuf::from("bar")),
};
assert_eq!(
vmm_action_from_request(parse_put_snapshot(&Body::new(body), Some("create")).unwrap()),
Expand Down
1 change: 1 addition & 0 deletions src/firecracker/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,7 @@ fn main_exec() -> Result<(), MainError> {
state: VmState::NotStarted,
vmm_version: FIRECRACKER_VERSION.to_string(),
app_name: "Firecracker".to_string(),
memory_regions: None,
};

if let Some(metrics_path) = arguments.single_value("metrics-path") {
Expand Down
Loading
Loading