Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
5 changes: 5 additions & 0 deletions .config/nextest.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[profile.ci]
fail-fast = false

[profile.ci.junit]
path = "junit.xml"
156 changes: 156 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,162 @@ jobs:
- name: Build iii-worker for target
run: cargo build -p iii-worker --target ${{ matrix.target }}

# ──────────────────────────────────────────────────────────────
# Worker Test Matrix (cross-platform integration tests)
# ──────────────────────────────────────────────────────────────

worker-test-matrix:
name: Worker Tests - ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
- os: macos-latest
steps:
- uses: actions/checkout@v4

- uses: dtolnay/rust-toolchain@stable

- uses: Swatinem/rust-cache@v2
with:
key: worker-test-${{ matrix.os }}

- uses: taiki-e/install-action@cargo-nextest

- name: Install system dependencies (Linux only)
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt-get update
sudo apt-get install -y libcap-ng-dev

- name: Run iii-worker tests
run: cargo nextest run -p iii-worker --profile ci

- uses: actions/upload-artifact@v4
if: always()
with:
name: junit-worker-test-${{ matrix.os }}
path: target/nextest/ci/junit.xml
retention-days: 7

# ──────────────────────────────────────────────────────────────
# Worker Feature-Gated Tests
# ──────────────────────────────────────────────────────────────

worker-test-vm-linux:
name: Worker Tests (VM) - linux
if: github.event_name == 'workflow_dispatch'
runs-on: [self-hosted, linux, kvm]
continue-on-error: true
steps:
- uses: actions/checkout@v4

- uses: dtolnay/rust-toolchain@stable

- uses: Swatinem/rust-cache@v2
with:
key: worker-test-vm-linux

- uses: taiki-e/install-action@cargo-nextest

- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y libcap-ng-dev

- name: Run VM feature-gated tests
run: cargo nextest run -p iii-worker --features integration-vm --profile ci

- uses: actions/upload-artifact@v4
if: always()
with:
name: junit-worker-test-vm-linux
path: target/nextest/ci/junit.xml
retention-days: 7

worker-test-vm-macos:
name: Worker Tests (VM) - macos
runs-on: macos-latest
continue-on-error: true
steps:
- uses: actions/checkout@v4

- uses: dtolnay/rust-toolchain@stable

- uses: Swatinem/rust-cache@v2
with:
key: worker-test-vm-macos

- uses: taiki-e/install-action@cargo-nextest

- name: Run VM feature-gated tests
run: cargo nextest run -p iii-worker --features integration-vm --profile ci

- uses: actions/upload-artifact@v4
if: always()
with:
name: junit-worker-test-vm-macos
path: target/nextest/ci/junit.xml
retention-days: 7

worker-test-oci-linux:
name: Worker Tests (OCI) - linux
if: github.event_name == 'workflow_dispatch'
runs-on: [self-hosted, linux, oci]
continue-on-error: true
steps:
- uses: actions/checkout@v4

- uses: dtolnay/rust-toolchain@stable

- uses: Swatinem/rust-cache@v2
with:
key: worker-test-oci-linux

- uses: taiki-e/install-action@cargo-nextest

- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y libcap-ng-dev

- name: Run OCI feature-gated tests
run: cargo nextest run -p iii-worker --features integration-oci --profile ci

- uses: actions/upload-artifact@v4
if: always()
with:
name: junit-worker-test-oci-linux
path: target/nextest/ci/junit.xml
retention-days: 7

worker-test-oci-macos:
name: Worker Tests (OCI) - macos
runs-on: macos-latest
continue-on-error: true
steps:
- uses: actions/checkout@v4

- uses: dtolnay/rust-toolchain@stable

- uses: Swatinem/rust-cache@v2
with:
key: worker-test-oci-macos

- uses: taiki-e/install-action@cargo-nextest

- name: Run OCI feature-gated tests
run: cargo nextest run -p iii-worker --features integration-oci --profile ci

- uses: actions/upload-artifact@v4
if: always()
with:
name: junit-worker-test-oci-macos
path: target/nextest/ci/junit.xml
retention-days: 7

# ──────────────────────────────────────────────────────────────
# SDK Node
# ──────────────────────────────────────────────────────────────
Expand Down
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions crates/iii-worker/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ path = "src/main.rs"
default = []
embed-libkrunfw = []
embed-init = ["iii-filesystem/embed-init"]
# Test-only features — gate heavy integration tests (never in default)
integration-vm = []
integration-oci = []

[dependencies]
iii-filesystem = { path = "../iii-filesystem" }
Expand Down
4 changes: 2 additions & 2 deletions crates/iii-worker/src/cli/binary_download.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ pub fn current_target() -> &'static str {
}

/// Returns the platform-appropriate archive extension.
fn archive_extension(target: &str) -> &'static str {
pub fn archive_extension(target: &str) -> &'static str {
if target.contains("windows") {
"zip"
} else {
Expand Down Expand Up @@ -107,7 +107,7 @@ pub fn checksum_download_url(
/// Extracts a named binary from a tar.gz archive.
///
/// Looks for an entry whose filename matches `binary_name` (ignoring directory prefixes).
fn extract_binary_from_targz(binary_name: &str, archive_bytes: &[u8]) -> Result<Vec<u8>, String> {
pub fn extract_binary_from_targz(binary_name: &str, archive_bytes: &[u8]) -> Result<Vec<u8>, String> {
let decoder = flate2::read::GzDecoder::new(archive_bytes);
let mut archive = tar::Archive::new(decoder);

Expand Down
2 changes: 1 addition & 1 deletion crates/iii-worker/src/cli/local_worker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ pub fn parse_manifest_resources(manifest_path: &Path) -> (u32, u32) {

/// Remove workspace contents except installed dependency directories.
/// This lets us re-copy source files without losing `npm install` artifacts.
fn clean_workspace_preserving_deps(workspace: &Path) {
pub fn clean_workspace_preserving_deps(workspace: &Path) {
let preserve = ["node_modules", "target", ".venv", "__pycache__"];
if let Ok(entries) = std::fs::read_dir(workspace) {
for entry in entries.flatten() {
Expand Down
16 changes: 11 additions & 5 deletions crates/iii-worker/src/cli/vm_boot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ pub struct VmBootArgs {
}

/// Compose the full libkrunfw file path from the resolved directory and platform filename.
fn resolve_krunfw_file_path() -> Option<std::path::PathBuf> {
pub fn resolve_krunfw_file_path() -> Option<std::path::PathBuf> {
let dir = crate::cli::firmware::resolve::resolve_libkrunfw_dir()?;
let filename = crate::cli::firmware::constants::libkrunfw_filename();
let file_path = dir.join(&filename);
Expand Down Expand Up @@ -108,7 +108,7 @@ fn raise_fd_limit() {
}
}

fn shell_quote(s: &str) -> String {
pub fn shell_quote(s: &str) -> String {
if s.chars().all(|c| {
c.is_alphanumeric() || c == '-' || c == '_' || c == '/' || c == '.' || c == ':' || c == '='
}) {
Expand All @@ -118,7 +118,7 @@ fn shell_quote(s: &str) -> String {
}
}

fn build_worker_cmd(exec: &str, args: &[String]) -> String {
pub fn build_worker_cmd(exec: &str, args: &[String]) -> String {
if args.is_empty() {
shell_quote(exec)
} else {
Expand All @@ -130,6 +130,13 @@ fn build_worker_cmd(exec: &str, args: &[String]) -> String {
}
}

/// Rewrite localhost/loopback URLs to use the given gateway IP.
/// Used by the VM boot process to redirect traffic into the guest network.
pub fn rewrite_localhost(s: &str, gateway_ip: &str) -> String {
s.replace("://localhost:", &format!("://{}:", gateway_ip))
.replace("://127.0.0.1:", &format!("://{}:", gateway_ip))
}

/// Boot the VM. Called from `main()` when `__vm-boot` is parsed.
/// This function does NOT return -- `krun_start_enter` replaces the process.
pub fn run(args: &VmBootArgs) -> ! {
Expand Down Expand Up @@ -230,8 +237,7 @@ fn boot_vm(args: &VmBootArgs) -> Result<std::convert::Infallible, String> {
let gateway_ip = network.gateway_ipv4().to_string();

let rewrite_localhost = |s: &str| -> String {
s.replace("://localhost:", &format!("://{}:", gateway_ip))
.replace("://127.0.0.1:", &format!("://{}:", gateway_ip))
rewrite_localhost(s, &gateway_ip)
};
let worker_cmd = rewrite_localhost(&worker_cmd);

Expand Down
10 changes: 5 additions & 5 deletions crates/iii-worker/src/cli/worker_manager/libkrun.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,15 +202,15 @@ impl LibkrunAdapter {
Self
}

fn worker_dir(name: &str) -> PathBuf {
pub fn worker_dir(name: &str) -> PathBuf {
dirs::home_dir()
.unwrap_or_else(|| PathBuf::from("/tmp"))
.join(".iii")
.join("managed")
.join(name)
}

fn image_rootfs(image: &str) -> PathBuf {
pub fn image_rootfs(image: &str) -> PathBuf {
let hash = {
use sha2::Digest;
let mut hasher = sha2::Sha256::new();
Expand All @@ -224,11 +224,11 @@ impl LibkrunAdapter {
.join(hash)
}

fn pid_file(name: &str) -> PathBuf {
pub fn pid_file(name: &str) -> PathBuf {
Self::worker_dir(name).join("vm.pid")
}

fn logs_dir(name: &str) -> PathBuf {
pub fn logs_dir(name: &str) -> PathBuf {
Self::worker_dir(name).join("logs")
}

Expand Down Expand Up @@ -524,7 +524,7 @@ This image likely does not publish arm64. Rebuild/push a multi-arch image (linux
}
}

fn k8s_mem_to_mib(value: &str) -> Option<String> {
pub fn k8s_mem_to_mib(value: &str) -> Option<String> {
if let Some(n) = value.strip_suffix("Mi") {
Some(n.to_string())
} else if let Some(n) = value.strip_suffix("Gi") {
Expand Down
Loading
Loading