Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
101 commits
Select commit Hold shift + click to select a range
66494ee
Fix clippy warnings
ejc3 Dec 24, 2025
8aabb99
Add registry-based layer caching for container builds
ejc3 Dec 24, 2025
52660e3
Add zstd to container for kernel tarball extraction
ejc3 Dec 24, 2025
0ef4d2d
Update podman on buildjet runner for cache flag support
ejc3 Dec 24, 2025
a49f74a
Run VM tests directly on bare metal, not in container
ejc3 Dec 24, 2025
715359a
Remove ulimit nproc and pids-limit (not allowed in GitHub Actions)
ejc3 Dec 24, 2025
d786fb5
Run all CI jobs on buildjet bare metal
ejc3 Dec 24, 2025
213db09
Simplify Makefile test targets
ejc3 Dec 24, 2025
ff88f37
Simplify CI with feature-based test gating
ejc3 Dec 24, 2025
bfb4f0a
Add explicit setup command and --setup flag for on-demand setup
ejc3 Dec 24, 2025
6ab93bf
Add setup-fcvm Makefile target as prerequisite for tests
ejc3 Dec 24, 2025
86fe163
Update docs and Makefile for explicit setup workflow
ejc3 Dec 24, 2025
b0a293a
Add test speed tiers for faster CI iteration
ejc3 Dec 24, 2025
4ea3a49
Fix feature-based test tier gating for pjdfstest
ejc3 Dec 24, 2025
7de5dbb
Document build performance benchmarks
ejc3 Dec 24, 2025
1fbe1ef
Deduplicate README/DESIGN docs, fix inaccuracies
ejc3 Dec 24, 2025
9bbcc00
Refactor test tiers: only test-root uses sudo
ejc3 Dec 25, 2025
0bd1acb
Add parallel in-VM pjdfstest matrix, remove sequential tests
ejc3 Dec 25, 2025
8a9aee7
Update docs and cross-reference pjdfstest matrices
ejc3 Dec 25, 2025
24e4389
Simplify Containerfile and Makefile for rootless container tests
ejc3 Dec 25, 2025
e91ca1c
Fix DNAT port collision and increase test timeouts
ejc3 Dec 25, 2025
62c9527
Increase FUSE reader threads from 1 to 256 in fc-agent
ejc3 Dec 25, 2025
90c1953
Add content-addressable image cache for localhost images
ejc3 Dec 25, 2025
6bf03e3
Simplify CI to 2 runners with sequential targets
ejc3 Dec 25, 2025
8ac3cba
Fix CI: use ubuntu-latest for container job (podman needs systemd ses…
ejc3 Dec 25, 2025
a709a36
CI: enable FUSE user_allow_other for tests
ejc3 Dec 25, 2025
fdca9de
Use .local/ for tests to support hardlinks on overlayfs
ejc3 Dec 25, 2025
9bad28b
Use CARGO_MANIFEST_DIR for .local test paths
ejc3 Dec 25, 2025
a458b69
Add diagnostics on hardlink test failure
ejc3 Dec 25, 2025
55ceef4
Fix hardlink unit test to simulate real FUSE behavior
ejc3 Dec 25, 2025
a5e821b
Add diagnostics and skip hardlink tests on unsupported filesystems
ejc3 Dec 25, 2025
316aefb
Fix hardlink tests: detect and skip when AT_EMPTY_PATH unavailable
ejc3 Dec 25, 2025
71f5aa9
Factor out AT_EMPTY_PATH check to common helper
ejc3 Dec 25, 2025
6e9eca4
Create target and cargo-home dirs before container run
ejc3 Dec 25, 2025
e9978f3
Create /mnt/fcvm-btrfs dir for container tests
ejc3 Dec 25, 2025
905df6e
Remove cargo-home mount from container tests
ejc3 Dec 25, 2025
a00d198
Set CARGO_HOME to writable location in container
ejc3 Dec 25, 2025
5c99851
Remove target dir mount from container
ejc3 Dec 25, 2025
526891a
Simplify container tests: run as root, remove userns
ejc3 Dec 25, 2025
f888b37
CI: Run setup-fcvm as explicit step before VM tests
ejc3 Dec 25, 2025
1bbe3b2
CI: Run full test suite in Container job too
ejc3 Dec 25, 2025
caadc31
CI: Configure rootless podman with cgroupfs on buildjet
ejc3 Dec 25, 2025
89033a0
CI: Fix containers.conf format (use [engine] section)
ejc3 Dec 25, 2025
71a2f7f
CI: Use printf for containers.conf (fix heredoc indentation)
ejc3 Dec 25, 2025
1c74bef
Print serial log on setup timeout for debugging
ejc3 Dec 25, 2025
2bfed99
Stream Layer 2 setup serial output at info level
ejc3 Dec 25, 2025
64f70c7
Fix Layer 2 setup: proper error handling and package resolution
ejc3 Dec 25, 2025
b0654f7
Update docs to reflect Layer 2 setup improvements
ejc3 Dec 25, 2025
f12d7d9
README: Expand setup section with detailed steps
ejc3 Dec 25, 2025
053cec8
README: Document --setup flag for auto-setup on first run
ejc3 Dec 25, 2025
8a90821
README: Clarify why --setup is rootless only
ejc3 Dec 25, 2025
0210025
CI: Auto-cancel in-progress runs on new push
ejc3 Dec 25, 2025
e3e75bc
CI: Add missing dependencies to Container job
ejc3 Dec 25, 2025
5f1853a
Fix VM shutdown in Layer 2 setup
ejc3 Dec 25, 2025
4b640fa
CI: Run setup inside container, add sanity checks
ejc3 Dec 25, 2025
62aeb8c
CI: Run setup inside container, add sanity checks
ejc3 Dec 25, 2025
7e4fa8b
Fix podman-in-podman for rootless container setup
ejc3 Dec 25, 2025
8059098
CI: Add Rust cache for faster builds
ejc3 Dec 25, 2025
b30f67a
CI: Add cargo cache for container builds
ejc3 Dec 25, 2025
676388d
Fix: Add --cgroups=disabled to actual podman command
ejc3 Dec 25, 2025
b27bb30
Refactor: Use single source for download script
ejc3 Dec 25, 2025
9290255
Separate lint tests from integration tests
ejc3 Dec 25, 2025
f0b9cec
CI: Install cargo-audit/deny for CVSS 4.0 support
ejc3 Dec 25, 2025
d98ed1d
docs: Add NO HACKS policy to CLAUDE.md
ejc3 Dec 25, 2025
a83bdbe
CI: Add shared-key to rust-cache for cache reuse
ejc3 Dec 25, 2025
d2b678a
CI: Save rust cache even on failure
ejc3 Dec 25, 2025
8e714fd
Fix disk space exhaustion in CI snapshot tests
ejc3 Dec 25, 2025
0cad682
CI: Enable userfaultfd in Container job
ejc3 Dec 25, 2025
39512ce
CI: Create and pass /dev/userfaultfd to container
ejc3 Dec 25, 2025
e99bb18
CI: Pin cargo tool versions for CVSS 4.0 support
ejc3 Dec 25, 2025
dad0a79
Remove userfaultfd device check - sysctl is sufficient
ejc3 Dec 25, 2025
62ec598
Fix formatting
ejc3 Dec 25, 2025
f054ea5
Add automatic debug logging to test files with CI artifact upload
ejc3 Dec 25, 2025
04769e0
Print debug log path at end of each fcvm process
ejc3 Dec 25, 2025
903474a
Update CLAUDE.md with debug logging design
ejc3 Dec 25, 2025
5929893
Fix lint failures: fmt and cargo-audit cache
ejc3 Dec 25, 2025
efebcee
Remove auto-add of --setup flag from tests
ejc3 Dec 26, 2025
58bca12
Add detailed UFFD copy error logging
ejc3 Dec 26, 2025
bc9c760
Add filtered dmesg to CI artifacts
ejc3 Dec 26, 2025
4544e28
Fix UFFD error logging: use Debug format to show errno
ejc3 Dec 26, 2025
1332748
Fix UFFD EEXIST handling for older kernels
ejc3 Dec 26, 2025
29ed0e2
CI: Add workflow_dispatch for manual triggers
ejc3 Dec 26, 2025
afedd80
docs: Add manual CI trigger to CLAUDE.md
ejc3 Dec 26, 2025
eee670a
Add XDG config file support for cargo install compatibility
ejc3 Dec 26, 2025
26a0a9a
Add Rust packaging metadata and shell completions
ejc3 Dec 26, 2025
ca8f46b
Add packaging integration tests
ejc3 Dec 26, 2025
1a16865
Add make test-packaging to verify cargo install works
ejc3 Dec 26, 2025
f161e33
Add packaging test to CI on default GitHub runner
ejc3 Dec 26, 2025
d75e8d0
Add lint job to CI on default GitHub runner
ejc3 Dec 26, 2025
d43040f
Add copy_file_range support to fuse-pipe
ejc3 Dec 26, 2025
fb2c689
Add inception support: configurable paths and container fixes
ejc3 Dec 26, 2025
879c155
Add test_kvm.rs to verify /dev/kvm in VM for inception
ejc3 Dec 26, 2025
1db9411
Add inception support: KVM-enabled kernel and /dev/kvm creation
ejc3 Dec 26, 2025
3957777
Add FUSE remap_file_range support for FICLONE/FICLONERANGE
ejc3 Dec 26, 2025
b5094ec
Add libfuse remap_file_range container test and fix opcode
ejc3 Dec 26, 2025
b1773fe
Use libfuse fork instead of patch in Containerfile
ejc3 Dec 26, 2025
0fddaef
Fix remap_file_range edge cases in kernel patch and libfuse
ejc3 Dec 26, 2025
83bc1b7
Add FICLONERANGE tests and enhance ficlone_test
ejc3 Dec 26, 2025
8575c31
Inline FUSE remap_file_range kernel patches in build.sh
ejc3 Dec 26, 2025
f7f8821
Make remap_file_range test skip gracefully without patched kernel
ejc3 Dec 26, 2025
08c1e5f
Handle cp --reflink exit code 1 as skip condition
ejc3 Dec 26, 2025
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
439 changes: 326 additions & 113 deletions .claude/CLAUDE.md

Large diffs are not rendered by default.

27 changes: 20 additions & 7 deletions .config/nextest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,23 +42,36 @@ retries = 0
[test-groups.stress-tests]
max-threads = 1

# Snapshot tests limited to 3 concurrent (each snapshot is ~5.6GB on disk)
[test-groups.snapshot-tests]
max-threads = 3

# VM tests run at full parallelism (num-cpus)
# Previously limited to 16 threads due to namespace holder process deaths,
# but root cause was rootless tests running under sudo. Now that privileged
# tests filter out rootless tests (-E '!test(/rootless/)'), full parallelism works.
[test-groups.vm-tests]
max-threads = "num-cpus"

[[profile.default.overrides]]
filter = "package(fcvm) & test(/stress_100/)"
test-group = "stress-tests"
slow-timeout = { period = "300s", terminate-after = 1 }
slow-timeout = { period = "600s", terminate-after = 1 }

# Snapshot tests: limited to 3 concurrent (each creates ~5.6GB snapshot on disk)
[[profile.default.overrides]]
filter = "package(fcvm) & (test(/snapshot/) | test(/clone/))"
test-group = "snapshot-tests"
slow-timeout = { period = "600s", terminate-after = 1 }

# VM tests get 10 minute timeout (non-snapshot tests)
[[profile.default.overrides]]
filter = "package(fcvm) & test(/test_/) & !test(/stress_100/) & !test(/pjdfstest_vm/) & !test(/snapshot/) & !test(/clone/)"
test-group = "vm-tests"
slow-timeout = { period = "600s", terminate-after = 1 }

# VM tests run with limited parallelism to avoid resource exhaustion
# In-VM pjdfstest needs 15 minutes (image import via FUSE over vsock is slow)
[[profile.default.overrides]]
filter = "package(fcvm) & test(/test_/) & !test(/stress_100/)"
filter = "package(fcvm) & test(/pjdfstest_vm/)"
test-group = "vm-tests"
slow-timeout = { period = "300s", terminate-after = 1 }
slow-timeout = { period = "900s", terminate-after = 1 }

# fuse-pipe tests can run with full parallelism
[[profile.default.overrides]]
Expand Down
187 changes: 175 additions & 12 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
name: CI

on:
workflow_dispatch:
pull_request:
branches: [main]
push:
branches: [main]

# Cancel in-progress runs when a new revision is pushed
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
CARGO_TERM_COLOR: always
FUSE_BACKEND_RS: ${{ github.workspace }}/fuse-backend-rs
FUSER: ${{ github.workspace }}/fuser
CONTAINER_ARCH: x86_64

jobs:
container-rootless:
name: Container (rootless)
# Runner 0: Lint (default GitHub runner, no KVM needed)
# Fast checks: fmt, clippy, audit, deny
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand All @@ -30,12 +38,34 @@ jobs:
repository: ejc3/fuser
ref: master
path: fuser
- name: make ci-container-rootless
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy
- uses: Swatinem/rust-cache@v2
with:
workspaces: fcvm -> target
- name: Install build dependencies
run: sudo apt-get update && sudo apt-get install -y libfuse3-dev libclang-dev clang
- name: Install cargo tools
run: cargo install cargo-audit cargo-deny --locked
- name: Check formatting
working-directory: fcvm
run: cargo fmt --all --check
- name: Clippy
working-directory: fcvm
run: cargo clippy --all-targets -- -D warnings
- name: Audit
working-directory: fcvm
run: cargo audit
- name: Deny
working-directory: fcvm
run: make ci-container-rootless
run: cargo deny check

container-sudo:
name: Container (sudo)
# Runner 0b: Packaging (default GitHub runner, no KVM needed)
# Verifies cargo install works without source tree access
packaging:
name: Packaging
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand All @@ -51,12 +81,24 @@ jobs:
repository: ejc3/fuser
ref: master
path: fuser
- name: make ci-container-sudo
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
workspaces: fcvm -> target
- name: Install build dependencies
run: sudo apt-get update && sudo apt-get install -y libfuse3-dev libclang-dev clang
- name: Build release
working-directory: fcvm
run: cargo build --release -p fcvm
- name: Test packaging
working-directory: fcvm
run: make ci-container-sudo
run: ./scripts/test-packaging.sh ./target/release/fcvm

vm:
name: Host (sudo+rootless)
# Runner 1: Host (bare metal with KVM)
# Runs: test-unit → test-fast → test-root (sequential)
host:
name: Host
runs-on: buildjet-32vcpu-ubuntu-2204
steps:
- uses: actions/checkout@v4
Expand All @@ -72,6 +114,34 @@ jobs:
repository: ejc3/fuser
ref: master
path: fuser
- name: Install Rust
run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- uses: Swatinem/rust-cache@v2
with:
cache-provider: buildjet
workspaces: fcvm -> target
cache-on-failure: "true"
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y fuse3 libfuse3-dev libclang-dev clang musl-tools \
iproute2 iptables slirp4netns dnsmasq qemu-utils e2fsprogs parted \
podman skopeo busybox-static cpio zstd autoconf automake libtool
- name: Install Firecracker
run: |
curl -L -o /tmp/firecracker.tgz \
https://github.com/firecracker-microvm/firecracker/releases/download/v1.14.0/firecracker-v1.14.0-x86_64.tgz
sudo tar -xzf /tmp/firecracker.tgz -C /usr/local/bin --strip-components=1 \
release-v1.14.0-x86_64/firecracker-v1.14.0-x86_64 \
release-v1.14.0-x86_64/jailer-v1.14.0-x86_64
sudo mv /usr/local/bin/firecracker-v1.14.0-x86_64 /usr/local/bin/firecracker
sudo mv /usr/local/bin/jailer-v1.14.0-x86_64 /usr/local/bin/jailer
- name: Install cargo tools
# cargo-audit >= 0.22.0 required for CVSS 4.0 support
# Use --force to override any stale cached versions
run: cargo install cargo-nextest@0.9.115 cargo-audit@0.22.0 cargo-deny@0.18.9 --locked --force
- name: Setup KVM and networking
run: |
sudo chmod 666 /dev/kvm
Expand All @@ -83,6 +153,99 @@ jobs:
fi
sudo chmod 666 /dev/userfaultfd
sudo sysctl -w vm.unprivileged_userfaultfd=1
- name: make container-test-vm
# Enable FUSE allow_other for tests
echo "user_allow_other" | sudo tee /etc/fuse.conf
- name: Create test log directory
run: mkdir -p /tmp/fcvm-test-logs
- name: test-unit
working-directory: fcvm
run: make test-unit
- name: setup-fcvm
working-directory: fcvm
run: make setup-fcvm
- name: test-fast
working-directory: fcvm
run: make test-fast
- name: test-root
working-directory: fcvm
run: make test-root
- name: Capture kernel logs
if: always()
run: |
# Filter dmesg for UFFD/memory/VM related messages only
sudo dmesg | grep -iE 'userfault|uffd|kvm|firecracker|oom|killed|segfault|page.fault' > /tmp/fcvm-test-logs/dmesg-filtered.log || true
- name: Upload test logs
if: always()
uses: actions/upload-artifact@v4
with:
name: test-logs-host
path: /tmp/fcvm-test-logs/
if-no-files-found: ignore
retention-days: 7

# Runner 2: Container (podman)
# Runs same tests as Host but inside a container
# Needs KVM for VM tests (container mounts /dev/kvm)
container:
name: Container
runs-on: buildjet-32vcpu-ubuntu-2204
steps:
- uses: actions/checkout@v4
with:
path: fcvm
- uses: actions/checkout@v4
with:
repository: ejc3/fuse-backend-rs
ref: master
path: fuse-backend-rs
- uses: actions/checkout@v4
with:
repository: ejc3/fuser
ref: master
path: fuser
- name: Setup KVM and rootless podman
run: |
sudo chmod 666 /dev/kvm
# Enable userfaultfd syscall for snapshot cloning
sudo sysctl -w vm.unprivileged_userfaultfd=1
# Configure rootless podman to use cgroupfs (no systemd session on CI)
mkdir -p ~/.config/containers
printf '[engine]\ncgroup_manager = "cgroupfs"\nevents_logger = "file"\n' > ~/.config/containers/containers.conf
# Create cargo cache directory for container
mkdir -p ${{ github.workspace }}/cargo-cache/registry ${{ github.workspace }}/cargo-cache/target
- name: Cache container cargo
uses: actions/cache@v4
with:
path: ${{ github.workspace }}/cargo-cache
key: container-cargo-${{ hashFiles('fcvm/Cargo.lock') }}
restore-keys: container-cargo-
- name: Create test log directory
run: mkdir -p /tmp/fcvm-test-logs
- name: container-test-unit
env:
CARGO_CACHE_DIR: ${{ github.workspace }}/cargo-cache
working-directory: fcvm
run: make container-test-unit
- name: container-setup-fcvm
env:
CARGO_CACHE_DIR: ${{ github.workspace }}/cargo-cache
working-directory: fcvm
run: make container-setup-fcvm
- name: container-test
env:
CARGO_CACHE_DIR: ${{ github.workspace }}/cargo-cache
working-directory: fcvm
run: make container-test-vm
run: make container-test
- name: Capture kernel logs
if: always()
run: |
# Filter dmesg for UFFD/memory/VM related messages only
sudo dmesg | grep -iE 'userfault|uffd|kvm|firecracker|oom|killed|segfault|page.fault' > /tmp/fcvm-test-logs/dmesg-filtered.log || true
- name: Upload test logs
if: always()
uses: actions/upload-artifact@v4
with:
name: test-logs-container
path: /tmp/fcvm-test-logs/
if-no-files-found: ignore
retention-days: 7
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ sync-test/
# Local settings (machine-specific)
*.local.*
*.local
cargo-home/
.local/
6 changes: 5 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,16 @@ Have an idea? [Open an issue](https://github.com/ejc3/fcvm/issues/new) describin
# Build everything
make build

# First-time setup (downloads kernel + creates rootfs, ~5-10 min)
make setup-btrfs
fcvm setup

# Run lints (must pass before PR)
make lint

# Run tests
make test # fuse-pipe tests
make test-vm # VM integration tests (requires KVM)
make test-root # VM tests (requires sudo + KVM)

# Format code
make fmt
Expand Down
Loading
Loading