Skip to content
Open
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
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.git
.DS_Store
**/*.log
110 changes: 110 additions & 0 deletions .github/workflows/docker-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
name: docker-ci

on:
push:
branches: ["**"]
pull_request:
workflow_dispatch:
inputs:
kernel_release:
description: "v9fs/test release tag providing the kernel Image (default: kernel-main)"
required: true
default: "kernel-main"
type: string

env:
# Pin the kernel version so BuildKit layer caching remains stable across commits.
# Bump this intentionally when you want to test a newer kernel.
KERNEL9P_LINUX_VERSION: "7.0"

jobs:
docker-test:
name: docker (${{ matrix.target }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
target: [test, race]

steps:
- name: Checkout
uses: actions/checkout@v4

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

- name: Build ${{ matrix.target }} image
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
target: ${{ matrix.target }}
load: true
tags: go9p:${{ matrix.target }}
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Run ${{ matrix.target }}
run: docker run --rm go9p:${{ matrix.target }}

examplefs-unit:
name: example fs unit tests
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
- name: Run unit tests for example filesystems
run: go test ./p/srv/examples/...

tlsramfs-e2e:
name: tlsramfs userspace e2e
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod
- name: Run tlsramfs TLS client CRUD
run: go test -count=1 ./p/srv/examples/tlsramfs -run TestTLSRamfs_TLSClientCRUD

kernel9p-qemu:
# arm64-only kernel-client testing using v9fs/docker methodology.
name: kernel9p-qemu (arm64, v9fs/docker, ${{ matrix.fs }})
strategy:
fail-fast: false
matrix:
include:
- arch: arm64
runs_on: ubuntu-24.04-arm
fs: ufs
- arch: arm64
runs_on: ubuntu-24.04-arm
fs: ramfs
- arch: arm64
runs_on: ubuntu-24.04-arm
fs: clonefs
- arch: arm64
runs_on: ubuntu-24.04-arm
fs: timefs
runs-on: ${{ matrix.runs_on }}
timeout-minutes: 90

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Run v9fs/docker kernel-client e2e (${{ matrix.fs }})
run: |
docker run --rm --privileged --platform linux/arm64 \
-v "${{ github.workspace }}:/opt/v9fs/go9p" \
-w /opt/v9fs/go9p \
-e V9FS_TEST_KERNEL_TAG="${{ inputs.kernel_release || 'kernel-main' }}" \
ghcr.io/v9fs/docker:v2.0.0 \
bash /opt/v9fs/go9p/scripts/v9fs/ci-e2e-fs.sh "${{ matrix.fs }}"

57 changes: 57 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# AGENTS.md

This file captures the working preferences for AI agents contributing to this repo.
Tweak freely.

## Branching and change hygiene

- Do active work on `rework` unless told otherwise.
- Keep `README.md`, `CHANGES.md`, and `TODO.md` updated as changes land.
- Do not commit generated outputs (`logs/`, `kernel/`, `tmp/`, `initrd.cpio`, pid files).

## Test philosophy
- Prefer **guest-direct execution** (Option A): run tests **inside the QEMU guest**.
- Avoid SSH/port-forwarding flows unless explicitly requested.
- use u-root based minimal initrd as root filesystem
- use u-root/cpu with NFS option to expose tools, benchmarks, tests, and results directories to the guest running in qemu
- be able to run as github actions or using act locally
- provide easy mechanism for running local tests (make or script based)
- verify workflow locally before pushing to github
- CI should surface failures (red dashboards) while still running the full suite:
- Run the whole matrix
- Record failures
- Exit non-zero at the end

## Local/dev environment assumptions

- Primary local dev is **macOS via Docker + QEMU**.
- Prefer solutions that work on Docker Desktop (no reliance on KVM).
- After any manual experiment that uses `docker run`, ensure containers are not left running:
- Prefer `docker run --rm` plus a project label `v9fs.harness=v9fs-test`.
- If you suspect a hung run left containers behind, clean up with `make docker-clean` or `./scripts/v9fs-docker-clean`.

## CI architecture preferences
- Use http://github.com/v9fs/docker published base image instead of building custom docker for kernel build and/or test frameworks
- Default to **ARM64** (`ubuntu-24.04-arm`) for builds/tests unless asked otherwise.
- Build and/or test will be triggered by external triggers (such as v9fs/linux changes) or user request in addition to any changes to this repo
- Separate concerns:
- **Kernel publishing** workflow: builds `v9fs/linux` arm64 `Image` and publishes it.
- **Harness CI** workflows: download a published kernel `Image` and run tests.
- Publishing:
- Prefer a stable, `wget`-able GitHub Release asset `Image` tagged `kernel-main`, `kernel-nightly`, or `kernel- `.
- GHCR is optional/secondary; keep it consistent if used.

## Logging and debuggability

- Always preserve logs for failures (artifact upload `if: always()`).
- When tests fail, also dump the relevant tails into the CI console output:
- `logs/*/qemu.log`
- per-test `*.log`
- `guest.exitcode` markers (or equivalent)

## Style

- Prefer small, explicit scripts over complex magic.
- Keep paths stable and explicit (`/workspaces/share`, `kernel/.build/...`).
- Avoid large refactors unless requested; preserve working behavior first.

25 changes: 25 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
### Go / tooling

- Add `go.mod` and enable module-based builds (`module github.com/lionkov/go9p`)
- Target modern Go (`go 1.26.0`) and set `toolchain go1.26.0`

### Tests / CI hygiene

- Fix `go test ./...` failures on modern Go:
- Resolve `go vet` “non-constant format string” warnings
- Make Unix socket-based tests portable by using a temp socket path instead of `net.Listen("unix", "")`
- Avoid flaky failures when shutting down the listener (ignore expected “use of closed network connection” on close)
- Add Docker-based Linux test runner (`Dockerfile`) with `test` and `race` targets.
- Add GitHub Actions CI that runs Docker-based tests on every push/PR.
- Add end-to-end client/server integration tests:
- UFS-backed e2e test in `p/clnt`
- Fsrv synthetic-tree e2e test in `p/srv`
- Add QEMU-based Linux kernel 9p client smoke test (`Dockerfile.kernel9p-qemu`) and run it in CI (arm64 only for now).
- Switch the kernel-client CI harness to run inside the prebuilt `ghcr.io/v9fs/docker:v2.0.0` image, using a u-root initrd + chroot flow (modeled after `github.com/v9fs/test`), instead of building a bespoke v9fs Docker image.
- Add per-example filesystem tests:
- Unit tests for each 9P server in `p/srv/examples/*`.
- Kernel-client QEMU e2e matrix for `ufs`, `ramfs`, `clonefs`, and `timefs`.
- Userspace TLS e2e stage for `tlsramfs`.
- CI: pin kernel9p Docker build and `docker run` to `linux/${{ matrix.arch }}` so Buildx does not load the wrong CPU architecture on split amd64/arm64 runners.
- CI: build the kernel9p Docker image **once per architecture** per workflow run, then run `qemu` / `diod` / `u9fs` smoke tests against that image (still uses BuildKit GHA cache across commits).

30 changes: 30 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
## Dockerfile for running Linux builds/tests from macOS.
##
## Usage:
## docker build -t go9p:test --target test .
## docker run --rm go9p:test
##
## Optional:
## docker build -t go9p:race --target race .
## docker run --rm go9p:race

ARG GO_VERSION=1.26.0

FROM golang:${GO_VERSION}-bookworm AS base
WORKDIR /src

# Keep the module download layer stable.
COPY go.mod ./
RUN go mod download

COPY . .

FROM base AS test
RUN go test ./...
CMD ["go", "test", "./..."]

FROM base AS race
# Race detector requires CGO; Debian-based images support this out of the box.
RUN go test -race ./...
CMD ["go", "test", "-race", "./..."]

168 changes: 168 additions & 0 deletions Dockerfile.kernel9p-qemu
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
## QEMU + upstream Linux kernel + kernel 9p client smoke test.
##
## This builds:
## - a recent Linux kernel with virtio + 9p enabled
## - a tiny initramfs containing busybox, an init script, and the test binary
## - then boots QEMU and runs the smoke test against a virtio-9p export
##
## Usage (from repo root):
## docker build -f Dockerfile.kernel9p-qemu --target kernel9p-test .
##
## Override kernel version:
## docker build -f Dockerfile.kernel9p-qemu --target kernel9p-test --build-arg LINUX_VERSION=7.0 .

ARG GO_VERSION=1.26.0
ARG LINUX_VERSION=7.0
ARG KERNEL_ARCH=amd64

FROM golang:${GO_VERSION}-bookworm AS gotools
ARG KERNEL_ARCH
WORKDIR /src
COPY go.mod ./
RUN go mod download
COPY . .

# Build a static-ish linux binary to run inside the guest.
RUN case "${KERNEL_ARCH}" in \
amd64) GOARCH=amd64 ;; \
arm64) GOARCH=arm64 ;; \
*) echo "unsupported KERNEL_ARCH=${KERNEL_ARCH}" >&2; exit 2 ;; \
esac \
&& CGO_ENABLED=0 GOOS=linux GOARCH="${GOARCH}" go build -o /out/kernel9p-smoke ./cmd/kernel9p-smoke \
&& CGO_ENABLED=0 GOOS=linux GOARCH="${GOARCH}" go build -o /out/go9p-ufs ./p/srv/examples/ufs

FROM debian:bookworm AS kernel
ARG LINUX_VERSION
ARG KERNEL_ARCH
WORKDIR /work

RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates git curl xz-utils \
build-essential bc bison flex \
libssl-dev libelf-dev \
cpio gzip \
&& rm -rf /var/lib/apt/lists/*

# Fetch kernel source via shallow git checkout of the target tag.
# This is generally more resilient in CI than downloading large tarballs.
RUN git clone --depth 1 --branch "v${LINUX_VERSION}" --single-branch \
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git linux

WORKDIR /work/linux

# Configure a minimal kernel suitable for QEMU + virtio + 9p.
RUN if [ "${KERNEL_ARCH}" = "arm64" ]; then \
make ARCH=arm64 defconfig ; \
else \
make defconfig ; \
fi \
&& ./scripts/config --enable CONFIG_BLK_DEV_INITRD \
&& ./scripts/config --enable CONFIG_DEVTMPFS \
&& ./scripts/config --enable CONFIG_DEVTMPFS_MOUNT \
&& ./scripts/config --enable CONFIG_VIRTIO_PCI \
&& ./scripts/config --enable CONFIG_VIRTIO_BLK \
&& ./scripts/config --enable CONFIG_VIRTIO_NET \
&& ./scripts/config --enable CONFIG_NET_9P \
&& ./scripts/config --enable CONFIG_NET_9P_VIRTIO \
&& ./scripts/config --enable CONFIG_9P_FS \
&& ./scripts/config --enable CONFIG_9P_FS_POSIX_ACL \
&& ./scripts/config --enable CONFIG_TMPFS \
&& ./scripts/config --enable CONFIG_TMPFS_POSIX_ACL \
&& if [ "${KERNEL_ARCH}" = "arm64" ]; then \
make ARCH=arm64 olddefconfig ; \
else \
make olddefconfig ; \
fi

RUN if [ "${KERNEL_ARCH}" = "arm64" ]; then \
make ARCH=arm64 -j"$(nproc)" Image ; \
else \
make -j"$(nproc)" bzImage ; \
fi

# Prebuilt kernel from v9fs/test GitHub releases.
FROM debian:bookworm AS kernel-v9fs
ARG KERNEL_ARCH
ARG V9FS_TEST_KERNEL_TAG=kernel-main
WORKDIR /work

RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates curl \
&& rm -rf /var/lib/apt/lists/* \
&& case "${KERNEL_ARCH}" in \
arm64) asset="Image" ;; \
amd64) asset="bzImage" ;; \
*) echo "unsupported KERNEL_ARCH=${KERNEL_ARCH}" >&2; exit 2 ;; \
esac \
&& curl -fsSL "https://github.com/v9fs/test/releases/download/${V9FS_TEST_KERNEL_TAG}/${asset}" -o "/work/${asset}" \
# Ensure both paths exist so downstream COPY steps don't need conditionals.
&& if [ "${asset}" = "Image" ]; then : > /work/bzImage; else : > /work/Image; fi

FROM debian:bookworm AS initramfs
WORKDIR /work

RUN apt-get update && apt-get install -y --no-install-recommends \
busybox-static cpio gzip \
&& rm -rf /var/lib/apt/lists/*

COPY --from=gotools /out/kernel9p-smoke /rootfs/kernel9p-smoke
COPY scripts/kernel9p-init /rootfs/init

RUN chmod +x /rootfs/init /rootfs/kernel9p-smoke \
&& mkdir -p /rootfs/bin /rootfs/sbin /rootfs/proc /rootfs/sys /rootfs/dev /rootfs/mnt/9p \
&& ln -s /bin/busybox /rootfs/bin/sh \
&& (cd /rootfs && /bin/busybox --install -s bin) \
&& (cd /rootfs && find . -print0 | cpio --null -ov --format=newc) > /out.cpio \
&& gzip -9 < /out.cpio > /out.cpio.gz

FROM debian:bookworm AS kernel9p-runtime
ARG KERNEL_ARCH
# Pinned u9fs commit (unofficial-mirror/u9fs) for reproducible builds.
ARG U9FS_COMMIT=d65923fd17e8b158350d3ccd6a4e32b89b15014a
WORKDIR /work

RUN apt-get update && apt-get install -y --no-install-recommends \
qemu-system-x86 qemu-system-arm \
diod socat \
ca-certificates git make gcc libc6-dev \
&& rm -rf /var/lib/apt/lists/* \
&& git init /tmp/u9fs \
&& cd /tmp/u9fs \
&& git remote add origin https://github.com/unofficial-mirror/u9fs.git \
&& git fetch --depth 1 origin "${U9FS_COMMIT}" \
&& git checkout FETCH_HEAD \
&& make \
&& install -m0755 u9fs /usr/local/bin/u9fs \
&& rm -rf /tmp/u9fs

COPY --from=initramfs /out.cpio /work/initramfs.cpio
COPY --from=initramfs /out.cpio.gz /work/initramfs.cpio.gz
COPY scripts/kernel9p-qemu-run.sh /work/run.sh
COPY --from=gotools /out/go9p-ufs /work/go9p-ufs

RUN chmod +x /work/run.sh /work/go9p-ufs

# The QEMU virtio-9p export directory (server side).
RUN mkdir -p /work/share

ENV OUT_DIR=/work/out
ENV KERNEL_ARCH=${KERNEL_ARCH}
ENV INITRAMFS=/work/initramfs.cpio
ENV SHARE_DIR=/work/share

CMD ["/work/run.sh"]

FROM kernel9p-runtime AS kernel9p-test
COPY --from=kernel /work/linux/arch/x86/boot/bzImage /work/bzImage
COPY --from=kernel /work/linux/arch/arm64/boot/Image /work/Image
ENV KERNEL_BZIMAGE=/work/bzImage
ENV KERNEL_IMAGE=/work/Image

FROM kernel9p-runtime AS kernel9p-test-v9fs
ARG KERNEL_ARCH
ARG V9FS_TEST_KERNEL_TAG=kernel-main

COPY --from=kernel-v9fs /work/Image /work/Image
COPY --from=kernel-v9fs /work/bzImage /work/bzImage
ENV KERNEL_BZIMAGE=/work/bzImage
ENV KERNEL_IMAGE=/work/Image

Loading
Loading