From 92d729f5cf9861fcb4cda1374c76887fdc3a150c Mon Sep 17 00:00:00 2001 From: Jianfeng Jiang Date: Wed, 22 Apr 2026 03:02:30 +0000 Subject: [PATCH 01/20] ci: add Asterinas doc workflow replay --- .../workflows/test-asterinas-kata-docs.yml | 169 ++++++++ docs/asterinas-kata-doc-workflow-progress.md | 46 ++ docs/doc-in-asterinas-repo.md | 132 ++++++ tools/kata/README.md | 7 + tools/kata/interactive_doc_test.py | 400 ++++++++++++++++++ 5 files changed, 754 insertions(+) create mode 100644 .github/workflows/test-asterinas-kata-docs.yml create mode 100644 docs/asterinas-kata-doc-workflow-progress.md create mode 100644 docs/doc-in-asterinas-repo.md create mode 100644 tools/kata/interactive_doc_test.py diff --git a/.github/workflows/test-asterinas-kata-docs.yml b/.github/workflows/test-asterinas-kata-docs.yml new file mode 100644 index 000000000..4003bdc67 --- /dev/null +++ b/.github/workflows/test-asterinas-kata-docs.yml @@ -0,0 +1,169 @@ +name: Test | Asterinas Kata Docs + +on: + workflow_dispatch: + push: + branches: + - asterinas + +permissions: + contents: read + +jobs: + resolve-upstream-asterinas-image: + name: Resolve upstream Asterinas image + runs-on: ubuntu-latest + outputs: + asterinas_version: ${{ steps.resolve.outputs.asterinas_version }} + docker_image_version: ${{ steps.resolve.outputs.docker_image_version }} + steps: + - name: Resolve Upstream Asterinas Image Metadata + id: resolve + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: | + set -euo pipefail + + asterinas_version="$( + gh api repos/asterinas/asterinas/contents/VERSION?ref=main --jq .content | + base64 -d | tr -d '\n' + )" + docker_image_version="$( + gh api repos/asterinas/asterinas/contents/DOCKER_IMAGE_VERSION?ref=main --jq .content | + base64 -d | tr -d '\n' + )" + test -n "${asterinas_version}" + test -n "${docker_image_version}" + + echo "asterinas_version=${asterinas_version}" >> "${GITHUB_OUTPUT}" + echo "docker_image_version=${docker_image_version}" >> "${GITHUB_OUTPUT}" + { + echo '## Resolved upstream Asterinas image' + echo + echo "- \`VERSION\`: \`${asterinas_version}\`" + echo "- \`DOCKER_IMAGE_VERSION\`: \`${docker_image_version}\`" + echo "- Base image: \`asterinas/asterinas:${docker_image_version}\`" + } >> "${GITHUB_STEP_SUMMARY}" + + test-documented-end-user-flow: + name: Test documented end-user flow + needs: + - resolve-upstream-asterinas-image + runs-on: ubuntu-latest + timeout-minutes: 120 + env: + KATA_DOC_LOG_DIR: ${{ runner.temp }}/asterinas-kata-doc-logs/end-user + steps: + - uses: actions/checkout@v4 + + - name: Check Host Device Visibility + shell: bash + run: | + set -euxo pipefail + ls -l /dev/kvm /dev/vhost-net /dev/vhost-vsock /dev/vsock + + - name: Install pexpect if Needed + shell: bash + run: | + set -euxo pipefail + if ! python3 - <<'PY' + import importlib.util + import sys + sys.exit(0 if importlib.util.find_spec("pexpect") else 1) + PY + then + python3 -m pip install --user pexpect + fi + + - name: Pull Published Asterinas Kata Image + shell: bash + run: | + set -euxo pipefail + docker pull "asterinas/kata:${{ needs.resolve-upstream-asterinas-image.outputs.docker_image_version }}" + + - name: Replay Documented End-User Flow + shell: bash + run: | + set -euxo pipefail + python3 tools/kata/interactive_doc_test.py \ + --scenario end-user \ + --kata-image "asterinas/kata:${{ needs.resolve-upstream-asterinas-image.outputs.docker_image_version }}" \ + --log-dir "${KATA_DOC_LOG_DIR}" + + - name: Upload End-User Flow Logs + if: always() + uses: actions/upload-artifact@v4 + with: + name: asterinas-kata-doc-end-user-logs + path: ${{ env.KATA_DOC_LOG_DIR }} + if-no-files-found: ignore + + test-documented-kernel-developer-flow: + name: Test documented kernel-developer flow + needs: + - resolve-upstream-asterinas-image + runs-on: ubuntu-latest + timeout-minutes: 180 + env: + KATA_DOC_LOG_DIR: ${{ runner.temp }}/asterinas-kata-doc-logs/kernel-developer + KATA_STATIC_TARBALL_RELEASE_REPO: ${{ github.repository }} + steps: + - uses: actions/checkout@v4 + + - name: Check Out Asterinas Source + uses: actions/checkout@v4 + with: + repository: asterinas/asterinas + path: asterinas-src + + - name: Prepare Local Asterinas Tree + shell: bash + run: | + set -euxo pipefail + rm -rf "${HOME}/asterinas" + mkdir -p "${HOME}/asterinas" + rsync -a --delete "${GITHUB_WORKSPACE}/asterinas-src/" "${HOME}/asterinas/" + + - name: Check Host Device Visibility + shell: bash + run: | + set -euxo pipefail + ls -l /dev/kvm /dev/vhost-net /dev/vhost-vsock /dev/vsock + + - name: Install pexpect if Needed + shell: bash + run: | + set -euxo pipefail + if ! python3 - <<'PY' + import importlib.util + import sys + sys.exit(0 if importlib.util.find_spec("pexpect") else 1) + PY + then + python3 -m pip install --user pexpect + fi + + - name: Pull Published Asterinas Builder Image + shell: bash + run: | + set -euxo pipefail + docker pull "asterinas/asterinas:${{ needs.resolve-upstream-asterinas-image.outputs.docker_image_version }}" + + - name: Replay Documented Kernel-Developer Flow + shell: bash + run: | + set -euxo pipefail + python3 tools/kata/interactive_doc_test.py \ + --scenario kernel-developer \ + --asterinas-image "asterinas/asterinas:${{ needs.resolve-upstream-asterinas-image.outputs.docker_image_version }}" \ + --asterinas-dir "${HOME}/asterinas" \ + --log-dir "${KATA_DOC_LOG_DIR}" + + - name: Upload Kernel-Developer Flow Logs + if: always() + uses: actions/upload-artifact@v4 + with: + name: asterinas-kata-doc-kernel-developer-logs + path: ${{ env.KATA_DOC_LOG_DIR }} + if-no-files-found: ignore diff --git a/docs/asterinas-kata-doc-workflow-progress.md b/docs/asterinas-kata-doc-workflow-progress.md new file mode 100644 index 000000000..2d660cee8 --- /dev/null +++ b/docs/asterinas-kata-doc-workflow-progress.md @@ -0,0 +1,46 @@ +# Asterinas Kata Doc Workflow Progress + +## 2026-04-22 Initial implementation notes + +- Requirement focus: + - add a new workflow that follows `docs/doc-in-asterinas-repo.md` + - keep the overall documented flow, but correct commands that are currently off + - test the interactive `docker run -it` and inner `nerdctl run -it` behavior with `pexpect` + - mount the local Asterinas tree from `$HOME/asterinas` for the developer flow + - use local `asterinas/asterinas` and `asterinas/kata` images first, then wire the same logic into CI +- Document drift confirmed and corrected in the implementation plan: + - `docker run --cgroupns none` is invalid; use `--cgroupns host` + - the outer container needs `/dev/vhost-net` and `/dev/vsock` in addition to `/dev/kvm` and `/dev/vhost-vsock` + - helper script names are `tools/kata/kata_env.sh` and `tools/kata/kata_services.sh` + - the developer flow bind mount should be `-v "${ASTERINAS_SRC}:/root/asterinas"` + - the Kata config path should be `/etc/kata-containers/configuration.toml` +- Added `tools/kata/interactive_doc_test.py`: + - uses `pexpect` to drive the documented interactive flow instead of only calling the existing non-interactive smoke helper + - covers the end-user scenario on `asterinas/kata` + - covers the kernel-developer scenario on `asterinas/asterinas` + - validates the local kernel handoff path with `/root/asterinas/target/osdk/aster-kernel-osdk-bin.qemu_elf` +- Added `.github/workflows/test-asterinas-kata-docs.yml`: + - one job replays the documented end-user flow against `asterinas/kata` + - one job replays the documented kernel-developer flow against `asterinas/asterinas` + - both jobs install `pexpect` only when needed and upload transcripts as artifacts +- Local environment findings: + - `pexpect` is already available in the current local environment + - local `asterinas/asterinas:*` and `asterinas/kata:*` images are present + - the local Asterinas source tree exists at `/home/jianfeng/asterinas` + - the local machine exposes `/dev/kvm`, `/dev/vhost-net`, `/dev/vhost-vsock`, and `/dev/vsock` + - local Docker Hub access currently times out during the inner `nerdctl run` +- Local validation completed so far: + - the documented end-user flow passed locally with + `python3 tools/kata/interactive_doc_test.py --scenario end-user --workload-image docker.1ms.run/alpine:latest` + - this successfully covered the interactive outer `docker run -it`, `tools/kata/kata_services.sh start`, inner `nerdctl run -it`, and post-exit `nerdctl rm foo` sequence + - after cleaning the `pexpect` output parsing and passing outer env overrides explicitly, the same end-user replay was run again locally and still passed + - per the latest user instruction, the kernel-developer flow does not need to finish locally; CI is now the authoritative validation path for that scenario +- Local validation policy: + - local testing uses `docker.1ms.run/alpine:latest` as the workload image when Docker Hub is unreachable + - CI must keep using `docker.io/alpine:latest` +- CI wiring note: + - the kernel-developer workflow path exports `KATA_STATIC_TARBALL_RELEASE_REPO=${GITHUB_REPOSITORY}` so `tools/kata/kata_env.sh install` validates this repository's published Asterinas Kata release assets instead of defaulting to upstream `kata-containers/kata-containers` +- Next steps: + - run a final quick local end-user replay after the latest script cleanup + - static-check the new workflow and script changes + - push and wait for CI to verify both documented flows end to end diff --git a/docs/doc-in-asterinas-repo.md b/docs/doc-in-asterinas-repo.md new file mode 100644 index 000000000..c99b1caac --- /dev/null +++ b/docs/doc-in-asterinas-repo.md @@ -0,0 +1,132 @@ +Kata Containers is a VM-based container runtime. Each container runs inside its own virtual machine, so containers do not share the host kernel. + +Asterinas now supports Kata Containers and can serve as the guest kernel in common Kata-based scenarios. + +## For End Users +For end users, we provide the `asterinas/kata` container image, which already includes the dependencies and scripts required by Kata. + +You can try it with the following command: + +```bash +ASTERINAS_IMAGE_TAG=0.17.2-20260407 +docker run --rm -it \ + --cgroupns host \ + --privileged \ + --device /dev/kvm \ + --device /dev/vhost-net \ + --device /dev/vhost-vsock \ + --device /dev/vsock \ + --tmpfs /tmp:exec,mode=1777,size=8g \ + --tmpfs /var/lib/containerd:exec,mode=755,size=8g \ + asterinas/kata:${ASTERINAS_IMAGE_TAG} +``` + +After entering the container, start the background services required by Kata. At the moment, these services are `containerd` and `syslogd`: + +```bash +tools/kata/kata_services.sh start +``` + +You can check their status with the following command to make sure they started successfully: + +```bash +tools/kata/kata_services.sh status +``` + +Then you can use `nerdctl` with Kata to start an Alpine container: + +```bash +nerdctl run \ + --cgroup-manager cgroupfs \ + --net none \ + --runtime io.containerd.kata.v2 \ + --name foo \ + -it \ + docker.io/alpine:latest +``` + +If Docker Hub is temporarily unreachable during local validation, you can use +`docker.1ms.run/alpine:latest` instead. CI should keep using +`docker.io/alpine:latest`. + +Once the container starts, you will be inside Alpine. You can run the following commands to verify that you are no longer in the host environment: + +```bash +cat /proc/version +cat /etc/alpine-release +``` + +After you exit the container, remove it with: + +```bash +nerdctl rm foo +``` + +## For Kernel Developers +Kernel developers typically use the `asterinas/asterinas` container image. When starting the container, you also need to pass the additional arguments required by Kata: + +```bash +ASTERINAS_IMAGE_TAG=0.17.2-20260407 +ASTERINAS_SRC=$HOME/asterinas +KATA_SRC=$(git rev-parse --show-toplevel) +docker run --rm -it \ + --cgroupns host \ + --privileged \ + --device /dev/kvm \ + --device /dev/vhost-net \ + --device /dev/vhost-vsock \ + --device /dev/vsock \ + --tmpfs /tmp:exec,mode=1777,size=8g \ + --tmpfs /var/lib/containerd:exec,mode=755,size=8g \ + -v "${ASTERINAS_SRC}:/root/asterinas" \ + -v "${KATA_SRC}:/root/kata-containers" \ + -w /root/kata-containers \ + asterinas/asterinas:${ASTERINAS_IMAGE_TAG} +``` + +After entering the container, you will already be in `/root/kata-containers`. +The examples below assume the current kata-containers checkout is mounted there, +and that the local Asterinas tree is mounted at `/root/asterinas`. + +```bash +pwd +``` + +This script installs the files, scripts, and configuration needed to run Kata. After that, you can test Kata in the same way as an end user: + +```bash +# Install all dependencies +tools/kata/kata_env.sh install + +# Start background services +tools/kata/kata_services.sh start + +nerdctl run \ + --cgroup-manager cgroupfs \ + --net none \ + --runtime io.containerd.kata.v2 \ + --name foo \ + -it \ + docker.io/alpine:latest +``` + +If Docker Hub is temporarily unreachable during local validation, you can use +`docker.1ms.run/alpine:latest` here as well. CI should keep using +`docker.io/alpine:latest`. + +You can also point Kata to a locally built guest kernel. Edit +`/etc/kata-containers/configuration.toml` and set `kernel` to the path of your +local kernel image: + +```toml +[hypervisor.qemu] +kernel = "/root/asterinas/target/osdk/aster-kernel-osdk-bin.qemu_elf" +``` + +Build the kernel with: + +```bash +cd /root/asterinas && make kernel BOOT_METHOD=qemu-direct +``` + +Then start the container with `nerdctl` again, and Kata will boot using your local kernel. diff --git a/tools/kata/README.md b/tools/kata/README.md index f670fdbec..1583c1729 100644 --- a/tools/kata/README.md +++ b/tools/kata/README.md @@ -4,6 +4,9 @@ Kata helpers live here. - `run_kata.sh`: provides predefined `smoke`, `pass`, and `workload` Kata tasks - `kata_env.sh`: provides `install` and `check` for the shared Kata environment lifecycle - `kata_services.sh`: provides `start`, `stop`, and `status` for the background Kata smoke-test services +- `interactive_doc_test.py`: replays the documented `docker run -it` and + inner `nerdctl run -it` flows with `pexpect` for both the `asterinas/kata` + and `asterinas/asterinas` images - `check_overlayfs.sh`: probes whether the current host-side backing filesystem can support overlayfs `upperdir` and `workdir` for local Kata runs - `config/`: repo-owned Kata, CNI, `containerd`, and smoke-test config files used by the scripts @@ -26,6 +29,10 @@ Kata helpers live here. - Legacy `KATA_ALPINE_*` overrides still map to the new `KATA_TEST_*` names. - The default workload still pulls Alpine and runs `cat /etc/alpine-release`, but the image, in-container command, and output check are now script-configurable. +- `interactive_doc_test.py` uses `KATA_DOC_TEST_WORKLOAD_IMAGE`, which defaults + to `docker.io/alpine:latest`. For local validation in environments where + Docker Hub is unreachable, you can temporarily switch it to + `docker.1ms.run/alpine:latest`. ## Local virtio-fs note diff --git a/tools/kata/interactive_doc_test.py b/tools/kata/interactive_doc_test.py new file mode 100644 index 000000000..c50ca7128 --- /dev/null +++ b/tools/kata/interactive_doc_test.py @@ -0,0 +1,400 @@ +#!/usr/bin/env python3 + +from __future__ import annotations + +import argparse +import os +import pathlib +import re +import shlex +import subprocess +import sys +import uuid + +import pexpect + + +OUTER_PROMPT = "PEXPECT_OUTER> " +GUEST_PROMPT = "PEXPECT_GUEST> " +DEFAULT_KATA_IMAGE = "asterinas/kata:0.17.2-20260407" +DEFAULT_ASTERINAS_IMAGE = "asterinas/asterinas:0.17.2-20260407" +DEFAULT_WORKLOAD_IMAGE = "docker.io/alpine:latest" +REPO_ROOT = pathlib.Path(__file__).resolve().parents[2] +ANSI_ESCAPE_RE = re.compile(r"\x1b(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])") + + +class ScenarioError(RuntimeError): + pass + + +def strip_terminal_control_sequences(text: str) -> str: + return ANSI_ESCAPE_RE.sub("", text).replace("\r", "") + + +def clean_command_output(command: str, output: str) -> str: + cleaned_lines: list[str] = [] + + for raw_line in strip_terminal_control_sequences(output).splitlines(): + line = raw_line.strip() + if not line: + continue + + if line.startswith(OUTER_PROMPT.strip()): + line = line[len(OUTER_PROMPT.strip()) :].strip() + elif line.startswith(GUEST_PROMPT.strip()): + line = line[len(GUEST_PROMPT.strip()) :].strip() + + if not line: + continue + + if line == command: + continue + if line == "'": + continue + if line == "status=$?": + continue + if line.startswith("printf '__DOC_TEST_EXIT__"): + continue + if line.startswith("__DOC_TEST_EXIT__"): + continue + + cleaned_lines.append(line) + + return "\n".join(cleaned_lines).strip() + + +def outer_docker_env_args() -> list[str]: + args = ["-e", "TERM=dumb"] + passthrough_vars = ( + "KATA_FORCE_APT", + "KATA_PAYLOAD_IMAGE", + "KATA_STATIC_TARBALL_RELEASE_REPO", + "KATA_STATIC_TARBALL_SHA256", + "KATA_STATIC_TARBALL_SHA256_URL", + "KATA_STATIC_TARBALL_URL", + "KATA_VERSION", + "NERDCTL_VERSION", + ) + + for env_name in passthrough_vars: + env_value = os.environ.get(env_name) + if env_value: + args.extend(["-e", f"{env_name}={env_value}"]) + + return args + + +class DockerShell: + def __init__(self, name: str, docker_args: list[str], transcript_path: pathlib.Path) -> None: + self.name = name + self.docker_args = docker_args + self.transcript_path = transcript_path + self.child: pexpect.spawn | None = None + self.log_handle = None + self.prompt = OUTER_PROMPT + + def start(self) -> "DockerShell": + self.transcript_path.parent.mkdir(parents=True, exist_ok=True) + self.log_handle = self.transcript_path.open("w", encoding="utf-8") + subprocess.run( + ["docker", "rm", "-f", self.name], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + check=False, + ) + self.child = pexpect.spawn( + "docker", + self.docker_args, + encoding="utf-8", + codec_errors="replace", + echo=False, + timeout=60, + ) + self.child.logfile_read = self.log_handle + self.child.setwinsize(60, 200) + self.child.expect(r"[#$] ", timeout=120) + self.set_prompt(OUTER_PROMPT) + return self + + def close(self) -> None: + if self.child is not None: + self.child.close(force=True) + self.child = None + subprocess.run( + ["docker", "rm", "-f", self.name], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + check=False, + ) + if self.log_handle is not None: + self.log_handle.close() + self.log_handle = None + + def set_prompt(self, prompt: str) -> None: + assert self.child is not None + self.child.sendline(f"export PS1={shlex.quote(prompt)}") + self.child.expect(re.escape(prompt), timeout=30) + self.prompt = prompt + + def run(self, command: str, timeout: int = 300, check: bool = True) -> str: + assert self.child is not None + marker = f"__DOC_TEST_EXIT__{uuid.uuid4().hex}__:" + self.child.sendline(command) + self.child.sendline("status=$?") + self.child.sendline(f"printf '{marker}%s\\n' \"$status\"") + self.child.expect(re.compile(re.escape(marker) + r"(\d+)\r?\n"), timeout=timeout) + output = clean_command_output(command, self.child.before) + status = int(self.child.match.group(1)) + self.child.expect(re.escape(self.prompt), timeout=60) + if check and status != 0: + raise ScenarioError(f"Command failed with exit code {status}: {command}\n{output}") + return output + + def enter_guest(self, command: str, timeout: int = 900) -> None: + assert self.child is not None + self.child.sendline(command) + self.child.expect(r"[#$] ", timeout=timeout) + self.set_prompt(GUEST_PROMPT) + + def exit_guest(self) -> None: + assert self.child is not None + self.child.sendline("exit") + self.child.expect(re.escape(OUTER_PROMPT), timeout=120) + self.prompt = OUTER_PROMPT + + +def require_paths(paths: list[pathlib.Path]) -> None: + missing_paths = [str(path) for path in paths if not path.exists()] + if missing_paths: + raise ScenarioError(f"Missing required path(s): {', '.join(missing_paths)}") + + +def run_guest_workload(shell: DockerShell, workload_image: str, host_proc_version: str, container_name: str = "foo") -> tuple[str, str]: + shell.run(f"nerdctl rm -f {shlex.quote(container_name)} >/dev/null 2>&1 || true", check=False) + shell.enter_guest( + " ".join( + [ + "nerdctl", + "run", + "--cgroup-manager", + "cgroupfs", + "--net", + "none", + "--runtime", + "io.containerd.kata.v2", + "--name", + shlex.quote(container_name), + "-it", + shlex.quote(workload_image), + ] + ), + timeout=900, + ) + guest_proc_version = shell.run("cat /proc/version") + alpine_release = shell.run("cat /etc/alpine-release") + shell.exit_guest() + shell.run(f"nerdctl rm -f {shlex.quote(container_name)}") + + if guest_proc_version.strip() == host_proc_version.strip(): + raise ScenarioError("Guest /proc/version unexpectedly matches the outer container /proc/version") + if not alpine_release.strip(): + raise ScenarioError("Expected a non-empty /etc/alpine-release inside the guest workload") + + return guest_proc_version.strip(), alpine_release.strip() + + +def run_end_user_scenario(args: argparse.Namespace) -> pathlib.Path: + outer_name = f"kata-doc-end-user-{uuid.uuid4().hex[:12]}" + transcript_path = args.log_dir / "end-user.transcript.log" + shell = DockerShell( + outer_name, + [ + "run", + "--rm", + "-it", + "--name", + outer_name, + "--cgroupns", + "host", + "--privileged", + "--device", + "/dev/kvm", + "--device", + "/dev/vhost-net", + "--device", + "/dev/vhost-vsock", + "--device", + "/dev/vsock", + "--tmpfs", + "/tmp:exec,mode=1777,size=8g", + "--tmpfs", + "/var/lib/containerd:exec,mode=755,size=8g", + *outer_docker_env_args(), + args.kata_image, + ], + transcript_path, + ).start() + + try: + print(f"[end-user] using outer image: {args.kata_image}") + host_proc_version = shell.run("cat /proc/version") + shell.run("cd /root/asterinas") + shell.run("./tools/kata/kata_services.sh start", timeout=300) + status_output = shell.run("./tools/kata/kata_services.sh status") + if "Kata services are running." not in status_output: + raise ScenarioError(f"Unexpected service status output:\n{status_output}") + guest_proc_version, alpine_release = run_guest_workload(shell, args.workload_image, host_proc_version) + print(f"[end-user] guest /proc/version: {guest_proc_version}") + print(f"[end-user] alpine release: {alpine_release}") + shell.run("./tools/kata/kata_services.sh stop", timeout=300) + return transcript_path + finally: + shell.close() + + +def run_kernel_developer_scenario(args: argparse.Namespace) -> pathlib.Path: + require_paths([args.repo_dir, args.asterinas_dir]) + outer_name = f"kata-doc-kernel-dev-{uuid.uuid4().hex[:12]}" + transcript_path = args.log_dir / "kernel-developer.transcript.log" + shell = DockerShell( + outer_name, + [ + "run", + "--rm", + "-it", + "--name", + outer_name, + "--cgroupns", + "host", + "--privileged", + "--device", + "/dev/kvm", + "--device", + "/dev/vhost-net", + "--device", + "/dev/vhost-vsock", + "--device", + "/dev/vsock", + "--tmpfs", + "/tmp:exec,mode=1777,size=8g", + "--tmpfs", + "/var/lib/containerd:exec,mode=755,size=8g", + *outer_docker_env_args(), + "-v", + f"{args.asterinas_dir.resolve()}:/root/asterinas", + "-v", + f"{args.repo_dir.resolve()}:/root/kata-containers:ro", + "-w", + "/root/kata-containers", + args.asterinas_image, + ], + transcript_path, + ).start() + + try: + print(f"[kernel-developer] using outer image: {args.asterinas_image}") + print(f"[kernel-developer] mounted Asterinas tree: {args.asterinas_dir}") + host_proc_version = shell.run("cat /proc/version") + shell.run("test -d /root/kata-containers/tools/kata") + shell.run("./tools/kata/kata_env.sh install", timeout=3600) + shell.run("./tools/kata/kata_services.sh start", timeout=300) + status_output = shell.run("./tools/kata/kata_services.sh status") + if "Kata services are running." not in status_output: + raise ScenarioError(f"Unexpected service status output:\n{status_output}") + packaged_guest_proc_version, packaged_alpine_release = run_guest_workload( + shell, + args.workload_image, + host_proc_version, + "foo", + ) + print(f"[kernel-developer] packaged guest /proc/version: {packaged_guest_proc_version}") + print(f"[kernel-developer] packaged alpine release: {packaged_alpine_release}") + shell.run("cd /root/asterinas && make kernel BOOT_METHOD=qemu-direct", timeout=7200) + shell.run("test -f /root/asterinas/target/osdk/aster-kernel-osdk-bin.qemu_elf") + shell.run( + "sed -i 's#^kernel = \".*\"#kernel = \"/root/asterinas/target/osdk/aster-kernel-osdk-bin.qemu_elf\"#' " + "/etc/kata-containers/configuration.toml" + ) + shell.run( + "grep -F 'kernel = \"/root/asterinas/target/osdk/aster-kernel-osdk-bin.qemu_elf\"' " + "/etc/kata-containers/configuration.toml" + ) + local_guest_proc_version, local_alpine_release = run_guest_workload( + shell, + args.workload_image, + host_proc_version, + "foo", + ) + print(f"[kernel-developer] local-kernel guest /proc/version: {local_guest_proc_version}") + print(f"[kernel-developer] local-kernel alpine release: {local_alpine_release}") + shell.run("./tools/kata/kata_services.sh stop", timeout=300) + return transcript_path + finally: + shell.close() + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Replay the documented Asterinas Kata flows with pexpect") + parser.add_argument( + "--scenario", + choices=["end-user", "kernel-developer", "all"], + default=os.environ.get("KATA_DOC_TEST_SCENARIO", "all"), + ) + parser.add_argument( + "--kata-image", + default=os.environ.get("KATA_DOC_TEST_KATA_IMAGE", DEFAULT_KATA_IMAGE), + ) + parser.add_argument( + "--asterinas-image", + default=os.environ.get("KATA_DOC_TEST_ASTERINAS_IMAGE", DEFAULT_ASTERINAS_IMAGE), + ) + parser.add_argument( + "--workload-image", + default=os.environ.get("KATA_DOC_TEST_WORKLOAD_IMAGE", DEFAULT_WORKLOAD_IMAGE), + ) + parser.add_argument( + "--repo-dir", + type=pathlib.Path, + default=pathlib.Path(os.environ.get("KATA_DOC_TEST_REPO_DIR", str(REPO_ROOT))), + ) + parser.add_argument( + "--asterinas-dir", + type=pathlib.Path, + default=pathlib.Path(os.environ.get("KATA_DOC_TEST_ASTERINAS_DIR", str(pathlib.Path.home() / "asterinas"))), + ) + parser.add_argument( + "--log-dir", + type=pathlib.Path, + default=pathlib.Path(os.environ.get("KATA_DOC_TEST_LOG_DIR", "/tmp/asterinas-kata-doc-tests")), + ) + return parser.parse_args() + + +def main() -> int: + args = parse_args() + args.log_dir.mkdir(parents=True, exist_ok=True) + transcripts: list[pathlib.Path] = [] + + try: + if args.scenario in {"end-user", "all"}: + transcripts.append(run_end_user_scenario(args)) + if args.scenario in {"kernel-developer", "all"}: + transcripts.append(run_kernel_developer_scenario(args)) + except Exception as error: + print(f"interactive_doc_test failed: {error}", file=sys.stderr) + if transcripts: + print("available transcript(s):", file=sys.stderr) + for transcript in transcripts: + print(f" - {transcript}", file=sys.stderr) + else: + print(f"transcript directory: {args.log_dir}", file=sys.stderr) + return 1 + + print("interactive_doc_test passed") + for transcript in transcripts: + print(f"transcript: {transcript}") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) From 5be1f8718af10e0083ee3abebf7ade7da73d57b8 Mon Sep 17 00:00:00 2001 From: Jianfeng Jiang Date: Wed, 22 Apr 2026 03:06:22 +0000 Subject: [PATCH 02/20] ci: fix docs workflow temp path --- .github/workflows/test-asterinas-kata-docs.yml | 4 ++-- docs/asterinas-kata-doc-workflow-progress.md | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-asterinas-kata-docs.yml b/.github/workflows/test-asterinas-kata-docs.yml index 4003bdc67..d08c029c2 100644 --- a/.github/workflows/test-asterinas-kata-docs.yml +++ b/.github/workflows/test-asterinas-kata-docs.yml @@ -53,7 +53,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 120 env: - KATA_DOC_LOG_DIR: ${{ runner.temp }}/asterinas-kata-doc-logs/end-user + KATA_DOC_LOG_DIR: /tmp/asterinas-kata-doc-logs/end-user steps: - uses: actions/checkout@v4 @@ -106,7 +106,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 180 env: - KATA_DOC_LOG_DIR: ${{ runner.temp }}/asterinas-kata-doc-logs/kernel-developer + KATA_DOC_LOG_DIR: /tmp/asterinas-kata-doc-logs/kernel-developer KATA_STATIC_TARBALL_RELEASE_REPO: ${{ github.repository }} steps: - uses: actions/checkout@v4 diff --git a/docs/asterinas-kata-doc-workflow-progress.md b/docs/asterinas-kata-doc-workflow-progress.md index 2d660cee8..62e0230ed 100644 --- a/docs/asterinas-kata-doc-workflow-progress.md +++ b/docs/asterinas-kata-doc-workflow-progress.md @@ -40,6 +40,7 @@ - CI must keep using `docker.io/alpine:latest` - CI wiring note: - the kernel-developer workflow path exports `KATA_STATIC_TARBALL_RELEASE_REPO=${GITHUB_REPOSITORY}` so `tools/kata/kata_env.sh install` validates this repository's published Asterinas Kata release assets instead of defaulting to upstream `kata-containers/kata-containers` + - the first GitHub dispatch attempt exposed a GitHub expression-validation issue: `runner.temp` is not accepted in this workflow's job-level `env`, so the log directory paths were normalized to plain `/tmp/...` - Next steps: - run a final quick local end-user replay after the latest script cleanup - static-check the new workflow and script changes From a642f584b9b860748877dd84285fa8518b4443bc Mon Sep 17 00:00:00 2001 From: Jianfeng Jiang Date: Wed, 22 Apr 2026 03:09:40 +0000 Subject: [PATCH 03/20] ci: make docs workflow vsock optional --- .../workflows/test-asterinas-kata-docs.yml | 6 ++- docs/asterinas-kata-doc-workflow-progress.md | 1 + docs/doc-in-asterinas-repo.md | 6 ++- tools/kata/interactive_doc_test.py | 41 +++++++++++-------- 4 files changed, 34 insertions(+), 20 deletions(-) diff --git a/.github/workflows/test-asterinas-kata-docs.yml b/.github/workflows/test-asterinas-kata-docs.yml index d08c029c2..0b15f6135 100644 --- a/.github/workflows/test-asterinas-kata-docs.yml +++ b/.github/workflows/test-asterinas-kata-docs.yml @@ -61,7 +61,8 @@ jobs: shell: bash run: | set -euxo pipefail - ls -l /dev/kvm /dev/vhost-net /dev/vhost-vsock /dev/vsock + ls -l /dev/kvm /dev/vhost-net /dev/vhost-vsock + ls -l /dev/vsock || true - name: Install pexpect if Needed shell: bash @@ -129,7 +130,8 @@ jobs: shell: bash run: | set -euxo pipefail - ls -l /dev/kvm /dev/vhost-net /dev/vhost-vsock /dev/vsock + ls -l /dev/kvm /dev/vhost-net /dev/vhost-vsock + ls -l /dev/vsock || true - name: Install pexpect if Needed shell: bash diff --git a/docs/asterinas-kata-doc-workflow-progress.md b/docs/asterinas-kata-doc-workflow-progress.md index 62e0230ed..3517fda91 100644 --- a/docs/asterinas-kata-doc-workflow-progress.md +++ b/docs/asterinas-kata-doc-workflow-progress.md @@ -41,6 +41,7 @@ - CI wiring note: - the kernel-developer workflow path exports `KATA_STATIC_TARBALL_RELEASE_REPO=${GITHUB_REPOSITORY}` so `tools/kata/kata_env.sh install` validates this repository's published Asterinas Kata release assets instead of defaulting to upstream `kata-containers/kata-containers` - the first GitHub dispatch attempt exposed a GitHub expression-validation issue: `runner.temp` is not accepted in this workflow's job-level `env`, so the log directory paths were normalized to plain `/tmp/...` + - the first push run also showed that `/dev/vsock` is not guaranteed on GitHub-hosted runners, while `/dev/kvm`, `/dev/vhost-net`, and `/dev/vhost-vsock` are present; the workflow and `pexpect` driver now treat `/dev/vsock` as optional instead of mandatory - Next steps: - run a final quick local end-user replay after the latest script cleanup - static-check the new workflow and script changes diff --git a/docs/doc-in-asterinas-repo.md b/docs/doc-in-asterinas-repo.md index c99b1caac..dc799ac9b 100644 --- a/docs/doc-in-asterinas-repo.md +++ b/docs/doc-in-asterinas-repo.md @@ -15,7 +15,6 @@ docker run --rm -it \ --device /dev/kvm \ --device /dev/vhost-net \ --device /dev/vhost-vsock \ - --device /dev/vsock \ --tmpfs /tmp:exec,mode=1777,size=8g \ --tmpfs /var/lib/containerd:exec,mode=755,size=8g \ asterinas/kata:${ASTERINAS_IMAGE_TAG} @@ -75,7 +74,6 @@ docker run --rm -it \ --device /dev/kvm \ --device /dev/vhost-net \ --device /dev/vhost-vsock \ - --device /dev/vsock \ --tmpfs /tmp:exec,mode=1777,size=8g \ --tmpfs /var/lib/containerd:exec,mode=755,size=8g \ -v "${ASTERINAS_SRC}:/root/asterinas" \ @@ -114,6 +112,10 @@ If Docker Hub is temporarily unreachable during local validation, you can use `docker.1ms.run/alpine:latest` here as well. CI should keep using `docker.io/alpine:latest`. +If `/dev/vsock` also exists on your host, it is fine to pass it through too, +but the documented minimum setup only requires `/dev/kvm`, `/dev/vhost-net`, +and `/dev/vhost-vsock`. + You can also point Kata to a locally built guest kernel. Edit `/etc/kata-containers/configuration.toml` and set `kernel` to the path of your local kernel image: diff --git a/tools/kata/interactive_doc_test.py b/tools/kata/interactive_doc_test.py index c50ca7128..11da39a1d 100644 --- a/tools/kata/interactive_doc_test.py +++ b/tools/kata/interactive_doc_test.py @@ -84,6 +84,29 @@ def outer_docker_env_args() -> list[str]: return args +def outer_docker_device_args() -> list[str]: + args: list[str] = [] + required_devices = ( + "/dev/kvm", + "/dev/vhost-net", + "/dev/vhost-vsock", + ) + optional_devices = ( + "/dev/vsock", + ) + + for device_path in required_devices: + if not pathlib.Path(device_path).exists(): + raise ScenarioError(f"Required device is missing on the host: {device_path}") + args.extend(["--device", device_path]) + + for device_path in optional_devices: + if pathlib.Path(device_path).exists(): + args.extend(["--device", device_path]) + + return args + + class DockerShell: def __init__(self, name: str, docker_args: list[str], transcript_path: pathlib.Path) -> None: self.name = name @@ -217,14 +240,7 @@ def run_end_user_scenario(args: argparse.Namespace) -> pathlib.Path: "--cgroupns", "host", "--privileged", - "--device", - "/dev/kvm", - "--device", - "/dev/vhost-net", - "--device", - "/dev/vhost-vsock", - "--device", - "/dev/vsock", + *outer_docker_device_args(), "--tmpfs", "/tmp:exec,mode=1777,size=8g", "--tmpfs", @@ -267,14 +283,7 @@ def run_kernel_developer_scenario(args: argparse.Namespace) -> pathlib.Path: "--cgroupns", "host", "--privileged", - "--device", - "/dev/kvm", - "--device", - "/dev/vhost-net", - "--device", - "/dev/vhost-vsock", - "--device", - "/dev/vsock", + *outer_docker_device_args(), "--tmpfs", "/tmp:exec,mode=1777,size=8g", "--tmpfs", From 79893feb4a52230196a8568c2d67ea207ebecdc9 Mon Sep 17 00:00:00 2001 From: Jianfeng Jiang Date: Wed, 22 Apr 2026 03:42:26 +0000 Subject: [PATCH 04/20] ci: stream doc workflow progress --- .../workflows/test-asterinas-kata-docs.yml | 4 +- docs/asterinas-kata-doc-workflow-progress.md | 2 + tools/kata/interactive_doc_test.py | 69 ++++++++++++++----- 3 files changed, 56 insertions(+), 19 deletions(-) diff --git a/.github/workflows/test-asterinas-kata-docs.yml b/.github/workflows/test-asterinas-kata-docs.yml index 0b15f6135..ae0f333ee 100644 --- a/.github/workflows/test-asterinas-kata-docs.yml +++ b/.github/workflows/test-asterinas-kata-docs.yml @@ -87,7 +87,7 @@ jobs: shell: bash run: | set -euxo pipefail - python3 tools/kata/interactive_doc_test.py \ + python3 -u tools/kata/interactive_doc_test.py \ --scenario end-user \ --kata-image "asterinas/kata:${{ needs.resolve-upstream-asterinas-image.outputs.docker_image_version }}" \ --log-dir "${KATA_DOC_LOG_DIR}" @@ -156,7 +156,7 @@ jobs: shell: bash run: | set -euxo pipefail - python3 tools/kata/interactive_doc_test.py \ + python3 -u tools/kata/interactive_doc_test.py \ --scenario kernel-developer \ --asterinas-image "asterinas/asterinas:${{ needs.resolve-upstream-asterinas-image.outputs.docker_image_version }}" \ --asterinas-dir "${HOME}/asterinas" \ diff --git a/docs/asterinas-kata-doc-workflow-progress.md b/docs/asterinas-kata-doc-workflow-progress.md index 3517fda91..937b70ed4 100644 --- a/docs/asterinas-kata-doc-workflow-progress.md +++ b/docs/asterinas-kata-doc-workflow-progress.md @@ -42,6 +42,8 @@ - the kernel-developer workflow path exports `KATA_STATIC_TARBALL_RELEASE_REPO=${GITHUB_REPOSITORY}` so `tools/kata/kata_env.sh install` validates this repository's published Asterinas Kata release assets instead of defaulting to upstream `kata-containers/kata-containers` - the first GitHub dispatch attempt exposed a GitHub expression-validation issue: `runner.temp` is not accepted in this workflow's job-level `env`, so the log directory paths were normalized to plain `/tmp/...` - the first push run also showed that `/dev/vsock` is not guaranteed on GitHub-hosted runners, while `/dev/kvm`, `/dev/vhost-net`, and `/dev/vhost-vsock` are present; the workflow and `pexpect` driver now treat `/dev/vsock` as optional instead of mandatory + - based on user feedback, the `pexpect` driver now streams child output to stdout and prints explicit phase markers before long-running commands such as `kata_env.sh install`, `nerdctl run`, and `make kernel` + - the guest-entry helper now fails fast when `nerdctl run -it ...` falls back to the outer shell instead of opening the inner guest shell, so proxy/image failures no longer look like a generic hang - Next steps: - run a final quick local end-user replay after the latest script cleanup - static-check the new workflow and script changes diff --git a/tools/kata/interactive_doc_test.py b/tools/kata/interactive_doc_test.py index 11da39a1d..d30fade89 100644 --- a/tools/kata/interactive_doc_test.py +++ b/tools/kata/interactive_doc_test.py @@ -27,6 +27,20 @@ class ScenarioError(RuntimeError): pass +class TeeWriter: + def __init__(self, *streams) -> None: + self.streams = streams + + def write(self, data: str) -> None: + for stream in self.streams: + stream.write(data) + stream.flush() + + def flush(self) -> None: + for stream in self.streams: + stream.flush() + + def strip_terminal_control_sequences(text: str) -> str: return ANSI_ESCAPE_RE.sub("", text).replace("\r", "") @@ -115,6 +129,7 @@ def __init__(self, name: str, docker_args: list[str], transcript_path: pathlib.P self.child: pexpect.spawn | None = None self.log_handle = None self.prompt = OUTER_PROMPT + self.stream_output = os.environ.get("KATA_DOC_TEST_STREAM_OUTPUT", "1") not in {"0", "false", "FALSE", "no", "NO"} def start(self) -> "DockerShell": self.transcript_path.parent.mkdir(parents=True, exist_ok=True) @@ -133,7 +148,10 @@ def start(self) -> "DockerShell": echo=False, timeout=60, ) - self.child.logfile_read = self.log_handle + if self.stream_output: + self.child.logfile_read = TeeWriter(self.log_handle, sys.stdout) + else: + self.child.logfile_read = self.log_handle self.child.setwinsize(60, 200) self.child.expect(r"[#$] ", timeout=120) self.set_prompt(OUTER_PROMPT) @@ -176,7 +194,11 @@ def run(self, command: str, timeout: int = 300, check: bool = True) -> str: def enter_guest(self, command: str, timeout: int = 900) -> None: assert self.child is not None self.child.sendline(command) - self.child.expect(r"[#$] ", timeout=timeout) + match_index = self.child.expect([re.escape(OUTER_PROMPT), r"[#$] "], timeout=timeout) + if match_index == 0: + self.prompt = OUTER_PROMPT + failure_output = clean_command_output(command, self.child.before) + raise ScenarioError(f"Guest shell did not start successfully for command: {command}\n{failure_output}") self.set_prompt(GUEST_PROMPT) def exit_guest(self) -> None: @@ -192,7 +214,12 @@ def require_paths(paths: list[pathlib.Path]) -> None: raise ScenarioError(f"Missing required path(s): {', '.join(missing_paths)}") +def announce(message: str) -> None: + print(message, flush=True) + + def run_guest_workload(shell: DockerShell, workload_image: str, host_proc_version: str, container_name: str = "foo") -> tuple[str, str]: + announce(f"[guest] launching workload container {container_name} from {workload_image}") shell.run(f"nerdctl rm -f {shlex.quote(container_name)} >/dev/null 2>&1 || true", check=False) shell.enter_guest( " ".join( @@ -216,6 +243,7 @@ def run_guest_workload(shell: DockerShell, workload_image: str, host_proc_versio guest_proc_version = shell.run("cat /proc/version") alpine_release = shell.run("cat /etc/alpine-release") shell.exit_guest() + announce(f"[guest] removing workload container {container_name}") shell.run(f"nerdctl rm -f {shlex.quote(container_name)}") if guest_proc_version.strip() == host_proc_version.strip(): @@ -252,16 +280,18 @@ def run_end_user_scenario(args: argparse.Namespace) -> pathlib.Path: ).start() try: - print(f"[end-user] using outer image: {args.kata_image}") + announce(f"[end-user] using outer image: {args.kata_image}") host_proc_version = shell.run("cat /proc/version") shell.run("cd /root/asterinas") + announce("[end-user] starting Kata background services") shell.run("./tools/kata/kata_services.sh start", timeout=300) status_output = shell.run("./tools/kata/kata_services.sh status") if "Kata services are running." not in status_output: raise ScenarioError(f"Unexpected service status output:\n{status_output}") guest_proc_version, alpine_release = run_guest_workload(shell, args.workload_image, host_proc_version) - print(f"[end-user] guest /proc/version: {guest_proc_version}") - print(f"[end-user] alpine release: {alpine_release}") + announce(f"[end-user] guest /proc/version: {guest_proc_version}") + announce(f"[end-user] alpine release: {alpine_release}") + announce("[end-user] stopping Kata background services") shell.run("./tools/kata/kata_services.sh stop", timeout=300) return transcript_path finally: @@ -301,11 +331,13 @@ def run_kernel_developer_scenario(args: argparse.Namespace) -> pathlib.Path: ).start() try: - print(f"[kernel-developer] using outer image: {args.asterinas_image}") - print(f"[kernel-developer] mounted Asterinas tree: {args.asterinas_dir}") + announce(f"[kernel-developer] using outer image: {args.asterinas_image}") + announce(f"[kernel-developer] mounted Asterinas tree: {args.asterinas_dir}") host_proc_version = shell.run("cat /proc/version") shell.run("test -d /root/kata-containers/tools/kata") + announce("[kernel-developer] running tools/kata/kata_env.sh install") shell.run("./tools/kata/kata_env.sh install", timeout=3600) + announce("[kernel-developer] starting Kata background services") shell.run("./tools/kata/kata_services.sh start", timeout=300) status_output = shell.run("./tools/kata/kata_services.sh status") if "Kata services are running." not in status_output: @@ -316,10 +348,12 @@ def run_kernel_developer_scenario(args: argparse.Namespace) -> pathlib.Path: host_proc_version, "foo", ) - print(f"[kernel-developer] packaged guest /proc/version: {packaged_guest_proc_version}") - print(f"[kernel-developer] packaged alpine release: {packaged_alpine_release}") + announce(f"[kernel-developer] packaged guest /proc/version: {packaged_guest_proc_version}") + announce(f"[kernel-developer] packaged alpine release: {packaged_alpine_release}") + announce("[kernel-developer] building local kernel with make kernel BOOT_METHOD=qemu-direct") shell.run("cd /root/asterinas && make kernel BOOT_METHOD=qemu-direct", timeout=7200) shell.run("test -f /root/asterinas/target/osdk/aster-kernel-osdk-bin.qemu_elf") + announce("[kernel-developer] switching Kata to the locally built kernel") shell.run( "sed -i 's#^kernel = \".*\"#kernel = \"/root/asterinas/target/osdk/aster-kernel-osdk-bin.qemu_elf\"#' " "/etc/kata-containers/configuration.toml" @@ -334,8 +368,9 @@ def run_kernel_developer_scenario(args: argparse.Namespace) -> pathlib.Path: host_proc_version, "foo", ) - print(f"[kernel-developer] local-kernel guest /proc/version: {local_guest_proc_version}") - print(f"[kernel-developer] local-kernel alpine release: {local_alpine_release}") + announce(f"[kernel-developer] local-kernel guest /proc/version: {local_guest_proc_version}") + announce(f"[kernel-developer] local-kernel alpine release: {local_alpine_release}") + announce("[kernel-developer] stopping Kata background services") shell.run("./tools/kata/kata_services.sh stop", timeout=300) return transcript_path finally: @@ -390,18 +425,18 @@ def main() -> int: if args.scenario in {"kernel-developer", "all"}: transcripts.append(run_kernel_developer_scenario(args)) except Exception as error: - print(f"interactive_doc_test failed: {error}", file=sys.stderr) + announce(f"interactive_doc_test failed: {error}") if transcripts: - print("available transcript(s):", file=sys.stderr) + announce("available transcript(s):") for transcript in transcripts: - print(f" - {transcript}", file=sys.stderr) + announce(f" - {transcript}") else: - print(f"transcript directory: {args.log_dir}", file=sys.stderr) + announce(f"transcript directory: {args.log_dir}") return 1 - print("interactive_doc_test passed") + announce("interactive_doc_test passed") for transcript in transcripts: - print(f"transcript: {transcript}") + announce(f"transcript: {transcript}") return 0 From e9b04de715be879ebbc1e5a3e580984bb83fa5e3 Mon Sep 17 00:00:00 2001 From: Jianfeng Jiang Date: Wed, 22 Apr 2026 04:21:46 +0000 Subject: [PATCH 05/20] ci: speed up kata static install --- docs/asterinas-kata-doc-workflow-progress.md | 1 + tools/kata/kata_env.sh | 18 +++++++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/docs/asterinas-kata-doc-workflow-progress.md b/docs/asterinas-kata-doc-workflow-progress.md index 937b70ed4..452950280 100644 --- a/docs/asterinas-kata-doc-workflow-progress.md +++ b/docs/asterinas-kata-doc-workflow-progress.md @@ -44,6 +44,7 @@ - the first push run also showed that `/dev/vsock` is not guaranteed on GitHub-hosted runners, while `/dev/kvm`, `/dev/vhost-net`, and `/dev/vhost-vsock` are present; the workflow and `pexpect` driver now treat `/dev/vsock` as optional instead of mandatory - based on user feedback, the `pexpect` driver now streams child output to stdout and prints explicit phase markers before long-running commands such as `kata_env.sh install`, `nerdctl run`, and `make kernel` - the guest-entry helper now fails fast when `nerdctl run -it ...` falls back to the outer shell instead of opening the inner guest shell, so proxy/image failures no longer look like a generic hang + - CI logs showed the real long pole inside `tools/kata/kata_env.sh install`: the static tarball path unpacked into a temporary directory and then copied a second 4.7 GiB tree into `/opt`; this has now been collapsed into a direct `tar --zstd -xf ... -C / opt/kata` install path, with explicit progress messages around the install phases - Next steps: - run a final quick local end-user replay after the latest script cleanup - static-check the new workflow and script changes diff --git a/tools/kata/kata_env.sh b/tools/kata/kata_env.sh index 69c6bd632..d24d41a2c 100755 --- a/tools/kata/kata_env.sh +++ b/tools/kata/kata_env.sh @@ -431,23 +431,20 @@ install_kata_from_payload_image() { install_kata_from_static_tarball() { source_marker_path="${KATA_INSTALL_SOURCE_MARKER:-/opt/kata/.kata-install-source}" static_tarball_url="$(resolve_static_tarball_url)" - extract_dir="${KATA_STATIC_EXTRACT_DIR:-/tmp/kata-static-extract}" static_tarball_sha256="$(resolve_static_tarball_sha256)" static_tarball_path="$(prepare_cached_static_tarball)" - rm -rf "${extract_dir}" - install -d -m 0755 "${extract_dir}" - tar --zstd -xf "${static_tarball_path}" -C "${extract_dir}" - - test -d "${extract_dir}/opt/kata" + echo "Installing Kata static tarball from ${static_tarball_url}" rm -rf /opt/kata - cp -a "${extract_dir}/opt/kata" /opt/ + tar --zstd -xf "${static_tarball_path}" -C / opt/kata + test -d /opt/kata { printf 'static-tarball-url %s\n' "${static_tarball_url}" printf 'static-tarball-sha256 %s\n' "${static_tarball_sha256}" } > "${source_marker_path}" test -x /opt/kata/bin/kata-runtime test -x /opt/kata/bin/containerd-shim-kata-v2 + echo "Installed Kata static tarball into /opt/kata" } patch_qemu_config_for_asterinas() { @@ -545,9 +542,11 @@ wait_for_containerd_ready() { } run_install_task() { + echo "Installing required distro packages" install_required_packages if need_nerdctl_install; then + echo "Installing nerdctl ${NERDCTL_VERSION}" download_release_asset \ /tmp/nerdctl.tgz \ "https://github.com/containerd/nerdctl/releases/download/${NERDCTL_VERSION}/nerdctl-${NERDCTL_VERSION#v}-linux-amd64.tar.gz" @@ -555,6 +554,7 @@ run_install_task() { fi if should_install_crictl && need_crictl_install; then + echo "Installing crictl ${CRICTL_VERSION}" download_release_asset \ /tmp/crictl.tgz \ "https://github.com/kubernetes-sigs/cri-tools/releases/download/${CRICTL_VERSION}/crictl-${CRICTL_VERSION}-linux-amd64.tar.gz" @@ -563,10 +563,13 @@ run_install_task() { if need_kata_install; then if [ -n "${KATA_ASTERINAS_KERNEL_PATH:-}" ] && [ -f "${KATA_ASTERINAS_KERNEL_PATH}" ]; then + echo "Installing Kata with local Asterinas kernel overlay" install_kata_from_asterinas_kernel_overlay elif [ -n "${KATA_STATIC_TARBALL_URL:-}" ] || [ -n "${KATA_STATIC_TARBALL_RELEASE_REPO:-}" ]; then + echo "Installing Kata from static tarball" install_kata_from_static_tarball else + echo "Installing Kata from payload image" install_kata_from_payload_image fi fi @@ -574,6 +577,7 @@ run_install_task() { install -d -m 0755 /usr/local/bin ln -sf /opt/kata/bin/kata-runtime /usr/local/bin/kata-runtime ln -sf /opt/kata/bin/containerd-shim-kata-v2 /usr/local/bin/containerd-shim-kata-v2 + echo "Kata install task completed" } run_check_task() { From f55faec9ff8c83058571b821069cc2ffbd4a9779 Mon Sep 17 00:00:00 2001 From: Jianfeng Jiang Date: Wed, 22 Apr 2026 04:36:33 +0000 Subject: [PATCH 06/20] ci: fix kata static tar extraction --- docs/asterinas-kata-doc-workflow-progress.md | 1 + tools/kata/kata_env.sh | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/asterinas-kata-doc-workflow-progress.md b/docs/asterinas-kata-doc-workflow-progress.md index 452950280..1baad3a40 100644 --- a/docs/asterinas-kata-doc-workflow-progress.md +++ b/docs/asterinas-kata-doc-workflow-progress.md @@ -45,6 +45,7 @@ - based on user feedback, the `pexpect` driver now streams child output to stdout and prints explicit phase markers before long-running commands such as `kata_env.sh install`, `nerdctl run`, and `make kernel` - the guest-entry helper now fails fast when `nerdctl run -it ...` falls back to the outer shell instead of opening the inner guest shell, so proxy/image failures no longer look like a generic hang - CI logs showed the real long pole inside `tools/kata/kata_env.sh install`: the static tarball path unpacked into a temporary directory and then copied a second 4.7 GiB tree into `/opt`; this has now been collapsed into a direct `tar --zstd -xf ... -C / opt/kata` install path, with explicit progress messages around the install phases + - the next CI log sample exposed a follow-on bug in that optimization: the tarball did not contain a directly addressable `opt/kata` member for selective extraction, so the install now unpacks the verified tarball directly at `/` instead - Next steps: - run a final quick local end-user replay after the latest script cleanup - static-check the new workflow and script changes diff --git a/tools/kata/kata_env.sh b/tools/kata/kata_env.sh index d24d41a2c..8c868b4e8 100755 --- a/tools/kata/kata_env.sh +++ b/tools/kata/kata_env.sh @@ -436,7 +436,7 @@ install_kata_from_static_tarball() { echo "Installing Kata static tarball from ${static_tarball_url}" rm -rf /opt/kata - tar --zstd -xf "${static_tarball_path}" -C / opt/kata + tar --zstd -xf "${static_tarball_path}" -C / test -d /opt/kata { printf 'static-tarball-url %s\n' "${static_tarball_url}" From e688ff4fdbb32f6ea6601538c093053e592ab1f3 Mon Sep 17 00:00:00 2001 From: Jianfeng Jiang Date: Wed, 22 Apr 2026 04:44:11 +0000 Subject: [PATCH 07/20] ci: pre-resolve kata static release assets --- .../publish-asterinas-kata-image.yml | 25 ++++++++++++++++++ .../workflows/test-asterinas-kata-docs.yml | 26 +++++++++++++++++++ docs/asterinas-kata-doc-workflow-progress.md | 1 + tools/packaging/asterinas-kata/Dockerfile | 2 ++ 4 files changed, 54 insertions(+) diff --git a/.github/workflows/publish-asterinas-kata-image.yml b/.github/workflows/publish-asterinas-kata-image.yml index 41848ee64..a029060d9 100644 --- a/.github/workflows/publish-asterinas-kata-image.yml +++ b/.github/workflows/publish-asterinas-kata-image.yml @@ -39,6 +39,7 @@ jobs: docker_image_version: ${{ steps.resolve.outputs.docker_image_version }} kata_static_tarball_release_repo: ${{ steps.resolve.outputs.kata_static_tarball_release_repo }} kata_static_tarball_url: ${{ steps.resolve.outputs.kata_static_tarball_url }} + kata_static_tarball_sha256: ${{ steps.resolve.outputs.kata_static_tarball_sha256 }} should_push: ${{ steps.resolve.outputs.should_push }} steps: - name: Resolve Publish Inputs @@ -67,6 +68,27 @@ jobs: image_repository="${WORKFLOW_IMAGE_REPOSITORY:-${DEFAULT_IMAGE_REPOSITORY}}" kata_static_tarball_release_repo="${WORKFLOW_RELEASE_REPO:-${GITHUB_REPOSITORY}}" kata_static_tarball_url="${WORKFLOW_STATIC_TARBALL_URL:-}" + kata_static_tarball_sha256= + if [ -z "${kata_static_tarball_url}" ]; then + kata_static_tarball_url="$( + gh api "repos/${kata_static_tarball_release_repo}/releases/latest" --jq ' + .assets[] + | select(.name | test("^kata-static-.*-asterinas-amd64\\.tar\\.zst$")) + | .browser_download_url + ' | head -n 1 + )" + fi + test -n "${kata_static_tarball_url}" + kata_static_tarball_name="${kata_static_tarball_url##*/}" + kata_static_tarball_sha256="$( + gh api "repos/${kata_static_tarball_release_repo}/releases/latest" --jq ' + .assets[] + | select(.name | test("^kata-static-.*-asterinas-amd64\\.SHA256SUMS$")) + | .browser_download_url + ' | head -n 1 | + xargs curl -fsSL | + awk -v asset_name="${kata_static_tarball_name}" '$2 == asset_name { print $1; exit }' + )" should_push=false if [ -n "${DOCKERHUB_USERNAME}" ] && [ -n "${DOCKERHUB_TOKEN}" ]; then @@ -84,6 +106,7 @@ jobs: echo "docker_image_version=${docker_image_version}" >> "${GITHUB_OUTPUT}" echo "kata_static_tarball_release_repo=${kata_static_tarball_release_repo}" >> "${GITHUB_OUTPUT}" echo "kata_static_tarball_url=${kata_static_tarball_url}" >> "${GITHUB_OUTPUT}" + echo "kata_static_tarball_sha256=${kata_static_tarball_sha256}" >> "${GITHUB_OUTPUT}" echo "should_push=${should_push}" >> "${GITHUB_OUTPUT}" { @@ -99,6 +122,7 @@ jobs: else echo "- Explicit Kata tarball URL: not set" fi + echo "- Kata tarball SHA256: \`${kata_static_tarball_sha256}\`" echo "- Push enabled: \`${should_push}\`" } >> "${GITHUB_STEP_SUMMARY}" @@ -145,6 +169,7 @@ jobs: ASTERINAS_BASE_IMAGE=${{ env.ASTERINAS_BASE_IMAGE }} KATA_STATIC_TARBALL_RELEASE_REPO=${{ needs.resolve-upstream-asterinas-image.outputs.kata_static_tarball_release_repo }} KATA_STATIC_TARBALL_URL=${{ needs.resolve-upstream-asterinas-image.outputs.kata_static_tarball_url }} + KATA_STATIC_TARBALL_SHA256=${{ needs.resolve-upstream-asterinas-image.outputs.kata_static_tarball_sha256 }} ASTERINAS_RELEASE_VERSION=${{ needs.resolve-upstream-asterinas-image.outputs.asterinas_version }} ASTERINAS_DOCKER_IMAGE_VERSION=${{ needs.resolve-upstream-asterinas-image.outputs.docker_image_version }} diff --git a/.github/workflows/test-asterinas-kata-docs.yml b/.github/workflows/test-asterinas-kata-docs.yml index ae0f333ee..a10b33cea 100644 --- a/.github/workflows/test-asterinas-kata-docs.yml +++ b/.github/workflows/test-asterinas-kata-docs.yml @@ -16,6 +16,8 @@ jobs: outputs: asterinas_version: ${{ steps.resolve.outputs.asterinas_version }} docker_image_version: ${{ steps.resolve.outputs.docker_image_version }} + kata_static_tarball_url: ${{ steps.resolve.outputs.kata_static_tarball_url }} + kata_static_tarball_sha256: ${{ steps.resolve.outputs.kata_static_tarball_sha256 }} steps: - name: Resolve Upstream Asterinas Image Metadata id: resolve @@ -33,17 +35,39 @@ jobs: gh api repos/asterinas/asterinas/contents/DOCKER_IMAGE_VERSION?ref=main --jq .content | base64 -d | tr -d '\n' )" + kata_static_tarball_url="$( + gh api "repos/${GITHUB_REPOSITORY}/releases/latest" --jq ' + .assets[] + | select(.name | test("^kata-static-.*-asterinas-amd64\\.tar\\.zst$")) + | .browser_download_url + ' | head -n 1 + )" + test -n "${kata_static_tarball_url}" + kata_static_tarball_name="${kata_static_tarball_url##*/}" + kata_static_tarball_sha256="$( + gh api "repos/${GITHUB_REPOSITORY}/releases/latest" --jq ' + .assets[] + | select(.name | test("^kata-static-.*-asterinas-amd64\\.SHA256SUMS$")) + | .browser_download_url + ' | head -n 1 | + xargs curl -fsSL | + awk -v asset_name="${kata_static_tarball_name}" '$2 == asset_name { print $1; exit }' + )" test -n "${asterinas_version}" test -n "${docker_image_version}" + test -n "${kata_static_tarball_sha256}" echo "asterinas_version=${asterinas_version}" >> "${GITHUB_OUTPUT}" echo "docker_image_version=${docker_image_version}" >> "${GITHUB_OUTPUT}" + echo "kata_static_tarball_url=${kata_static_tarball_url}" >> "${GITHUB_OUTPUT}" + echo "kata_static_tarball_sha256=${kata_static_tarball_sha256}" >> "${GITHUB_OUTPUT}" { echo '## Resolved upstream Asterinas image' echo echo "- \`VERSION\`: \`${asterinas_version}\`" echo "- \`DOCKER_IMAGE_VERSION\`: \`${docker_image_version}\`" echo "- Base image: \`asterinas/asterinas:${docker_image_version}\`" + echo "- Kata static tarball: \`${kata_static_tarball_url}\`" } >> "${GITHUB_STEP_SUMMARY}" test-documented-end-user-flow: @@ -109,6 +133,8 @@ jobs: env: KATA_DOC_LOG_DIR: /tmp/asterinas-kata-doc-logs/kernel-developer KATA_STATIC_TARBALL_RELEASE_REPO: ${{ github.repository }} + KATA_STATIC_TARBALL_URL: ${{ needs.resolve-upstream-asterinas-image.outputs.kata_static_tarball_url }} + KATA_STATIC_TARBALL_SHA256: ${{ needs.resolve-upstream-asterinas-image.outputs.kata_static_tarball_sha256 }} steps: - uses: actions/checkout@v4 diff --git a/docs/asterinas-kata-doc-workflow-progress.md b/docs/asterinas-kata-doc-workflow-progress.md index 1baad3a40..543f6642b 100644 --- a/docs/asterinas-kata-doc-workflow-progress.md +++ b/docs/asterinas-kata-doc-workflow-progress.md @@ -46,6 +46,7 @@ - the guest-entry helper now fails fast when `nerdctl run -it ...` falls back to the outer shell instead of opening the inner guest shell, so proxy/image failures no longer look like a generic hang - CI logs showed the real long pole inside `tools/kata/kata_env.sh install`: the static tarball path unpacked into a temporary directory and then copied a second 4.7 GiB tree into `/opt`; this has now been collapsed into a direct `tar --zstd -xf ... -C / opt/kata` install path, with explicit progress messages around the install phases - the next CI log sample exposed a follow-on bug in that optimization: the tarball did not contain a directly addressable `opt/kata` member for selective extraction, so the install now unpacks the verified tarball directly at `/` instead + - the latest CI round also exposed an unrelated but real stability issue in `Publish | Asterinas Kata Image`: the Docker build resolved the latest release URL from inside the container and hit unauthenticated GitHub API rate limits, so the workflows now resolve the tarball URL and SHA256 up front and pass them into the container explicitly - Next steps: - run a final quick local end-user replay after the latest script cleanup - static-check the new workflow and script changes diff --git a/tools/packaging/asterinas-kata/Dockerfile b/tools/packaging/asterinas-kata/Dockerfile index f6773e0e3..647ded2d1 100644 --- a/tools/packaging/asterinas-kata/Dockerfile +++ b/tools/packaging/asterinas-kata/Dockerfile @@ -3,6 +3,7 @@ FROM ${ASTERINAS_BASE_IMAGE} ARG KATA_STATIC_TARBALL_RELEASE_REPO=kata-containers/kata-containers ARG KATA_STATIC_TARBALL_URL= +ARG KATA_STATIC_TARBALL_SHA256= ARG ASTERINAS_RELEASE_VERSION= ARG ASTERINAS_DOCKER_IMAGE_VERSION= @@ -12,6 +13,7 @@ COPY tools/kata /root/asterinas/tools/kata RUN KATA_STATIC_TARBALL_RELEASE_REPO="${KATA_STATIC_TARBALL_RELEASE_REPO}" \ KATA_STATIC_TARBALL_URL="${KATA_STATIC_TARBALL_URL}" \ + KATA_STATIC_TARBALL_SHA256="${KATA_STATIC_TARBALL_SHA256}" \ bash /root/asterinas/tools/kata/kata_env.sh install RUN printf '%s\n' "${ASTERINAS_RELEASE_VERSION}" > /root/asterinas/.asterinas-version && \ From f939047cbd8b7b5a5d2a8e963669a2109bcd0ab2 Mon Sep 17 00:00:00 2001 From: Jianfeng Jiang Date: Wed, 22 Apr 2026 04:49:04 +0000 Subject: [PATCH 08/20] ci: match release checksums by suffix --- .github/workflows/publish-asterinas-kata-image.yml | 2 +- .github/workflows/test-asterinas-kata-docs.yml | 2 +- docs/asterinas-kata-doc-workflow-progress.md | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/publish-asterinas-kata-image.yml b/.github/workflows/publish-asterinas-kata-image.yml index a029060d9..75f198eb7 100644 --- a/.github/workflows/publish-asterinas-kata-image.yml +++ b/.github/workflows/publish-asterinas-kata-image.yml @@ -87,7 +87,7 @@ jobs: | .browser_download_url ' | head -n 1 | xargs curl -fsSL | - awk -v asset_name="${kata_static_tarball_name}" '$2 == asset_name { print $1; exit }' + awk -v asset_name="${kata_static_tarball_name}" '$2 == asset_name || $2 ~ ("/" asset_name "$") { print $1; exit }' )" should_push=false diff --git a/.github/workflows/test-asterinas-kata-docs.yml b/.github/workflows/test-asterinas-kata-docs.yml index a10b33cea..f69ab320c 100644 --- a/.github/workflows/test-asterinas-kata-docs.yml +++ b/.github/workflows/test-asterinas-kata-docs.yml @@ -51,7 +51,7 @@ jobs: | .browser_download_url ' | head -n 1 | xargs curl -fsSL | - awk -v asset_name="${kata_static_tarball_name}" '$2 == asset_name { print $1; exit }' + awk -v asset_name="${kata_static_tarball_name}" '$2 == asset_name || $2 ~ ("/" asset_name "$") { print $1; exit }' )" test -n "${asterinas_version}" test -n "${docker_image_version}" diff --git a/docs/asterinas-kata-doc-workflow-progress.md b/docs/asterinas-kata-doc-workflow-progress.md index 543f6642b..fc11faea3 100644 --- a/docs/asterinas-kata-doc-workflow-progress.md +++ b/docs/asterinas-kata-doc-workflow-progress.md @@ -47,6 +47,7 @@ - CI logs showed the real long pole inside `tools/kata/kata_env.sh install`: the static tarball path unpacked into a temporary directory and then copied a second 4.7 GiB tree into `/opt`; this has now been collapsed into a direct `tar --zstd -xf ... -C / opt/kata` install path, with explicit progress messages around the install phases - the next CI log sample exposed a follow-on bug in that optimization: the tarball did not contain a directly addressable `opt/kata` member for selective extraction, so the install now unpacks the verified tarball directly at `/` instead - the latest CI round also exposed an unrelated but real stability issue in `Publish | Asterinas Kata Image`: the Docker build resolved the latest release URL from inside the container and hit unauthenticated GitHub API rate limits, so the workflows now resolve the tarball URL and SHA256 up front and pass them into the container explicitly + - after that pre-resolution change, the next push showed a format mismatch in the release `SHA256SUMS`: the checksum file records the tarball with a full path suffix instead of only the basename, so the workflow-side SHA lookup now accepts either an exact basename match or a trailing `/${asset_name}` - Next steps: - run a final quick local end-user replay after the latest script cleanup - static-check the new workflow and script changes From 7ddd8d6ed7aa2340ed4808fa3f985bbcfeb44b6c Mon Sep 17 00:00:00 2001 From: Jianfeng Jiang Date: Wed, 22 Apr 2026 05:07:51 +0000 Subject: [PATCH 09/20] ci: dump kata console logs on failure --- docs/asterinas-kata-doc-workflow-progress.md | 2 + tools/kata/interactive_doc_test.py | 90 ++++++++++++++------ 2 files changed, 68 insertions(+), 24 deletions(-) diff --git a/docs/asterinas-kata-doc-workflow-progress.md b/docs/asterinas-kata-doc-workflow-progress.md index fc11faea3..5660193c7 100644 --- a/docs/asterinas-kata-doc-workflow-progress.md +++ b/docs/asterinas-kata-doc-workflow-progress.md @@ -48,6 +48,8 @@ - the next CI log sample exposed a follow-on bug in that optimization: the tarball did not contain a directly addressable `opt/kata` member for selective extraction, so the install now unpacks the verified tarball directly at `/` instead - the latest CI round also exposed an unrelated but real stability issue in `Publish | Asterinas Kata Image`: the Docker build resolved the latest release URL from inside the container and hit unauthenticated GitHub API rate limits, so the workflows now resolve the tarball URL and SHA256 up front and pass them into the container explicitly - after that pre-resolution change, the next push showed a format mismatch in the release `SHA256SUMS`: the checksum file records the tarball with a full path suffix instead of only the basename, so the workflow-side SHA lookup now accepts either an exact basename match or a trailing `/${asset_name}` + - configuration comparison result: before the local-kernel override, both the end-user flow and the kernel-developer flow start Kata from the same default config target, `/opt/kata/share/defaults/kata-containers/configuration.toml -> configuration-asterinas.toml` + - on interactive failures, `tools/kata/interactive_doc_test.py` now also dumps `/tmp/kata-console.log`, `/tmp/console.log`, `/tmp/kata-qemu-serial.log`, `/tmp/qemu-serial.log`, `/tmp/containerd.log`, and `/tmp/kata-syslog.log` so QEMU-side failures are visible immediately - Next steps: - run a final quick local end-user replay after the latest script cleanup - static-check the new workflow and script changes diff --git a/tools/kata/interactive_doc_test.py b/tools/kata/interactive_doc_test.py index d30fade89..1240d2640 100644 --- a/tools/kata/interactive_doc_test.py +++ b/tools/kata/interactive_doc_test.py @@ -218,33 +218,73 @@ def announce(message: str) -> None: print(message, flush=True) +def dump_kata_debug_logs(shell: DockerShell, context: str) -> None: + announce(f"[debug] dumping Kata/QEMU logs after {context}") + for log_path in ( + "/tmp/kata-console.log", + "/tmp/console.log", + "/tmp/kata-qemu-serial.log", + "/tmp/qemu-serial.log", + "/tmp/containerd.log", + "/tmp/kata-syslog.log", + ): + output = shell.run( + f'test -f {shlex.quote(log_path)} && {{ echo "--- {log_path} ---"; tail -n 200 {shlex.quote(log_path)}; }} || true', + check=False, + timeout=60, + ) + if output: + announce(output) + + +def log_active_kata_configs(shell: DockerShell, label: str) -> None: + announce(f"[{label}] active Kata default config:") + announce( + shell.run( + "readlink -f /opt/kata/share/defaults/kata-containers/configuration.toml", + timeout=60, + ) + ) + announce(f"[{label}] active runtime config:") + announce( + shell.run( + "readlink -f /etc/kata-containers/configuration.toml 2>/dev/null || echo /etc/kata-containers/configuration.toml", + timeout=60, + ) + ) + + def run_guest_workload(shell: DockerShell, workload_image: str, host_proc_version: str, container_name: str = "foo") -> tuple[str, str]: announce(f"[guest] launching workload container {container_name} from {workload_image}") shell.run(f"nerdctl rm -f {shlex.quote(container_name)} >/dev/null 2>&1 || true", check=False) - shell.enter_guest( - " ".join( - [ - "nerdctl", - "run", - "--cgroup-manager", - "cgroupfs", - "--net", - "none", - "--runtime", - "io.containerd.kata.v2", - "--name", - shlex.quote(container_name), - "-it", - shlex.quote(workload_image), - ] - ), - timeout=900, - ) - guest_proc_version = shell.run("cat /proc/version") - alpine_release = shell.run("cat /etc/alpine-release") - shell.exit_guest() - announce(f"[guest] removing workload container {container_name}") - shell.run(f"nerdctl rm -f {shlex.quote(container_name)}") + try: + shell.enter_guest( + " ".join( + [ + "nerdctl", + "run", + "--cgroup-manager", + "cgroupfs", + "--net", + "none", + "--runtime", + "io.containerd.kata.v2", + "--name", + shlex.quote(container_name), + "-it", + shlex.quote(workload_image), + ] + ), + timeout=900, + ) + guest_proc_version = shell.run("cat /proc/version") + alpine_release = shell.run("cat /etc/alpine-release") + shell.exit_guest() + announce(f"[guest] removing workload container {container_name}") + shell.run(f"nerdctl rm -f {shlex.quote(container_name)}") + except Exception: + dump_kata_debug_logs(shell, f"failed nerdctl run for {container_name}") + raise if guest_proc_version.strip() == host_proc_version.strip(): raise ScenarioError("Guest /proc/version unexpectedly matches the outer container /proc/version") @@ -285,6 +325,7 @@ def run_end_user_scenario(args: argparse.Namespace) -> pathlib.Path: shell.run("cd /root/asterinas") announce("[end-user] starting Kata background services") shell.run("./tools/kata/kata_services.sh start", timeout=300) + log_active_kata_configs(shell, "end-user") status_output = shell.run("./tools/kata/kata_services.sh status") if "Kata services are running." not in status_output: raise ScenarioError(f"Unexpected service status output:\n{status_output}") @@ -339,6 +380,7 @@ def run_kernel_developer_scenario(args: argparse.Namespace) -> pathlib.Path: shell.run("./tools/kata/kata_env.sh install", timeout=3600) announce("[kernel-developer] starting Kata background services") shell.run("./tools/kata/kata_services.sh start", timeout=300) + log_active_kata_configs(shell, "kernel-developer") status_output = shell.run("./tools/kata/kata_services.sh status") if "Kata services are running." not in status_output: raise ScenarioError(f"Unexpected service status output:\n{status_output}") From e4d5a7c8d4da76019a56ac1a2965f646c500a1c4 Mon Sep 17 00:00:00 2001 From: Jianfeng Jiang Date: Wed, 22 Apr 2026 05:20:54 +0000 Subject: [PATCH 10/20] ci: prebuild asterinas kernel for docs flow --- .github/workflows/test-asterinas-kata-docs.yml | 10 ++++++++++ docs/asterinas-kata-doc-workflow-progress.md | 1 + 2 files changed, 11 insertions(+) diff --git a/.github/workflows/test-asterinas-kata-docs.yml b/.github/workflows/test-asterinas-kata-docs.yml index f69ab320c..3e9225e9f 100644 --- a/.github/workflows/test-asterinas-kata-docs.yml +++ b/.github/workflows/test-asterinas-kata-docs.yml @@ -178,6 +178,16 @@ jobs: set -euxo pipefail docker pull "asterinas/asterinas:${{ needs.resolve-upstream-asterinas-image.outputs.docker_image_version }}" + - name: Prebuild Local Asterinas Kernel + shell: bash + run: | + set -euxo pipefail + docker run --rm \ + -v "${HOME}/asterinas:/root/asterinas" \ + -w /root/asterinas \ + "asterinas/asterinas:${{ needs.resolve-upstream-asterinas-image.outputs.docker_image_version }}" \ + bash -lc 'make kernel BOOT_METHOD=qemu-direct' + - name: Replay Documented Kernel-Developer Flow shell: bash run: | diff --git a/docs/asterinas-kata-doc-workflow-progress.md b/docs/asterinas-kata-doc-workflow-progress.md index 5660193c7..d841690e0 100644 --- a/docs/asterinas-kata-doc-workflow-progress.md +++ b/docs/asterinas-kata-doc-workflow-progress.md @@ -50,6 +50,7 @@ - after that pre-resolution change, the next push showed a format mismatch in the release `SHA256SUMS`: the checksum file records the tarball with a full path suffix instead of only the basename, so the workflow-side SHA lookup now accepts either an exact basename match or a trailing `/${asset_name}` - configuration comparison result: before the local-kernel override, both the end-user flow and the kernel-developer flow start Kata from the same default config target, `/opt/kata/share/defaults/kata-containers/configuration.toml -> configuration-asterinas.toml` - on interactive failures, `tools/kata/interactive_doc_test.py` now also dumps `/tmp/kata-console.log`, `/tmp/console.log`, `/tmp/kata-qemu-serial.log`, `/tmp/qemu-serial.log`, `/tmp/containerd.log`, and `/tmp/kata-syslog.log` so QEMU-side failures are visible immediately + - to keep the interactive developer replay within the expected post-install budget, the CI job now prebuilds the mounted `$HOME/asterinas` kernel once before invoking the `pexpect` driver; the driver still runs the documented `make kernel BOOT_METHOD=qemu-direct` command inside the container, but that command should now hit an already-built tree and return quickly - Next steps: - run a final quick local end-user replay after the latest script cleanup - static-check the new workflow and script changes From 15cd778d5fc93fecd12111d2ad315e12300c0c67 Mon Sep 17 00:00:00 2001 From: Jianfeng Jiang Date: Wed, 22 Apr 2026 05:34:59 +0000 Subject: [PATCH 11/20] ci: fix interactive command marker handling --- docs/asterinas-kata-doc-workflow-progress.md | 1 + tools/kata/interactive_doc_test.py | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/asterinas-kata-doc-workflow-progress.md b/docs/asterinas-kata-doc-workflow-progress.md index d841690e0..3b7cd17ee 100644 --- a/docs/asterinas-kata-doc-workflow-progress.md +++ b/docs/asterinas-kata-doc-workflow-progress.md @@ -51,6 +51,7 @@ - configuration comparison result: before the local-kernel override, both the end-user flow and the kernel-developer flow start Kata from the same default config target, `/opt/kata/share/defaults/kata-containers/configuration.toml -> configuration-asterinas.toml` - on interactive failures, `tools/kata/interactive_doc_test.py` now also dumps `/tmp/kata-console.log`, `/tmp/console.log`, `/tmp/kata-qemu-serial.log`, `/tmp/qemu-serial.log`, `/tmp/containerd.log`, and `/tmp/kata-syslog.log` so QEMU-side failures are visible immediately - to keep the interactive developer replay within the expected post-install budget, the CI job now prebuilds the mounted `$HOME/asterinas` kernel once before invoking the `pexpect` driver; the driver still runs the documented `make kernel BOOT_METHOD=qemu-direct` command inside the container, but that command should now hit an already-built tree and return quickly + - a deeper replay hang was then traced to the Python driver itself rather than Kata: `DockerShell.run()` sent the target command, `status=$?`, and the exit-marker `printf` as three separate queued lines, which is fragile for long-running interactive commands. The driver now wraps each command and exit-marker emission into one shell line so it can no longer stall waiting for a marker that was never executed - Next steps: - run a final quick local end-user replay after the latest script cleanup - static-check the new workflow and script changes diff --git a/tools/kata/interactive_doc_test.py b/tools/kata/interactive_doc_test.py index 1240d2640..763abc068 100644 --- a/tools/kata/interactive_doc_test.py +++ b/tools/kata/interactive_doc_test.py @@ -180,11 +180,10 @@ def set_prompt(self, prompt: str) -> None: def run(self, command: str, timeout: int = 300, check: bool = True) -> str: assert self.child is not None marker = f"__DOC_TEST_EXIT__{uuid.uuid4().hex}__:" - self.child.sendline(command) - self.child.sendline("status=$?") - self.child.sendline(f"printf '{marker}%s\\n' \"$status\"") + wrapped_command = f"{command}; __doc_test_status=$?; printf '{marker}%s\\n' \"$__doc_test_status\"" + self.child.sendline(wrapped_command) self.child.expect(re.compile(re.escape(marker) + r"(\d+)\r?\n"), timeout=timeout) - output = clean_command_output(command, self.child.before) + output = clean_command_output(wrapped_command, self.child.before) status = int(self.child.match.group(1)) self.child.expect(re.escape(self.prompt), timeout=60) if check and status != 0: From 5bb9d71492ca287f246a10bc16578a6e05718c91 Mon Sep 17 00:00:00 2001 From: Jianfeng Jiang Date: Wed, 22 Apr 2026 05:43:45 +0000 Subject: [PATCH 12/20] ci: share asterinas build caches in docs flow --- .../workflows/test-asterinas-kata-docs.yml | 7 +++++++ docs/asterinas-kata-doc-workflow-progress.md | 1 + tools/kata/interactive_doc_test.py | 21 +++++++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/.github/workflows/test-asterinas-kata-docs.yml b/.github/workflows/test-asterinas-kata-docs.yml index 3e9225e9f..f0122b75c 100644 --- a/.github/workflows/test-asterinas-kata-docs.yml +++ b/.github/workflows/test-asterinas-kata-docs.yml @@ -135,6 +135,9 @@ jobs: KATA_STATIC_TARBALL_RELEASE_REPO: ${{ github.repository }} KATA_STATIC_TARBALL_URL: ${{ needs.resolve-upstream-asterinas-image.outputs.kata_static_tarball_url }} KATA_STATIC_TARBALL_SHA256: ${{ needs.resolve-upstream-asterinas-image.outputs.kata_static_tarball_sha256 }} + KATA_DOC_TEST_NIX_DIR: ${{ runner.temp }}/asterinas-kernel-cache/nix + KATA_DOC_TEST_CARGO_HOME: ${{ runner.temp }}/asterinas-kernel-cache/cargo + KATA_DOC_TEST_RUSTUP_HOME: ${{ runner.temp }}/asterinas-kernel-cache/rustup steps: - uses: actions/checkout@v4 @@ -182,8 +185,12 @@ jobs: shell: bash run: | set -euxo pipefail + mkdir -p "${KATA_DOC_TEST_NIX_DIR}" "${KATA_DOC_TEST_CARGO_HOME}" "${KATA_DOC_TEST_RUSTUP_HOME}" docker run --rm \ -v "${HOME}/asterinas:/root/asterinas" \ + -v "${KATA_DOC_TEST_NIX_DIR}:/nix" \ + -v "${KATA_DOC_TEST_CARGO_HOME}:/root/.cargo" \ + -v "${KATA_DOC_TEST_RUSTUP_HOME}:/root/.rustup" \ -w /root/asterinas \ "asterinas/asterinas:${{ needs.resolve-upstream-asterinas-image.outputs.docker_image_version }}" \ bash -lc 'make kernel BOOT_METHOD=qemu-direct' diff --git a/docs/asterinas-kata-doc-workflow-progress.md b/docs/asterinas-kata-doc-workflow-progress.md index 3b7cd17ee..b8f20b4a8 100644 --- a/docs/asterinas-kata-doc-workflow-progress.md +++ b/docs/asterinas-kata-doc-workflow-progress.md @@ -52,6 +52,7 @@ - on interactive failures, `tools/kata/interactive_doc_test.py` now also dumps `/tmp/kata-console.log`, `/tmp/console.log`, `/tmp/kata-qemu-serial.log`, `/tmp/qemu-serial.log`, `/tmp/containerd.log`, and `/tmp/kata-syslog.log` so QEMU-side failures are visible immediately - to keep the interactive developer replay within the expected post-install budget, the CI job now prebuilds the mounted `$HOME/asterinas` kernel once before invoking the `pexpect` driver; the driver still runs the documented `make kernel BOOT_METHOD=qemu-direct` command inside the container, but that command should now hit an already-built tree and return quickly - a deeper replay hang was then traced to the Python driver itself rather than Kata: `DockerShell.run()` sent the target command, `status=$?`, and the exit-marker `printf` as three separate queued lines, which is fragile for long-running interactive commands. The driver now wraps each command and exit-marker emission into one shell line so it can no longer stall waiting for a marker that was never executed + - the newest transcript showed the remaining long pole after `kata_env.sh install`: the interactive `make kernel BOOT_METHOD=qemu-direct` reinitialized `/nix`, cargo, and rustup state inside the developer container. The workflow now prebuilds with shared cache mounts and passes the same cache mounts into the interactive container so the documented in-container `make kernel` can reuse the same build state - Next steps: - run a final quick local end-user replay after the latest script cleanup - static-check the new workflow and script changes diff --git a/tools/kata/interactive_doc_test.py b/tools/kata/interactive_doc_test.py index 763abc068..df0f5f6b1 100644 --- a/tools/kata/interactive_doc_test.py +++ b/tools/kata/interactive_doc_test.py @@ -121,6 +121,26 @@ def outer_docker_device_args() -> list[str]: return args +def kernel_developer_cache_mount_args() -> list[str]: + args: list[str] = [] + cache_mounts = ( + ("KATA_DOC_TEST_NIX_DIR", "/nix"), + ("KATA_DOC_TEST_CARGO_HOME", "/root/.cargo"), + ("KATA_DOC_TEST_RUSTUP_HOME", "/root/.rustup"), + ) + + for env_name, container_path in cache_mounts: + host_path = os.environ.get(env_name) + if not host_path: + continue + + host_dir = pathlib.Path(host_path) + host_dir.mkdir(parents=True, exist_ok=True) + args.extend(["-v", f"{host_dir.resolve()}:{container_path}"]) + + return args + + class DockerShell: def __init__(self, name: str, docker_args: list[str], transcript_path: pathlib.Path) -> None: self.name = name @@ -359,6 +379,7 @@ def run_kernel_developer_scenario(args: argparse.Namespace) -> pathlib.Path: "--tmpfs", "/var/lib/containerd:exec,mode=755,size=8g", *outer_docker_env_args(), + *kernel_developer_cache_mount_args(), "-v", f"{args.asterinas_dir.resolve()}:/root/asterinas", "-v", From fc8b10c75220acb048320cf300b4efcceb82d585 Mon Sep 17 00:00:00 2001 From: Jianfeng Jiang Date: Wed, 22 Apr 2026 05:45:26 +0000 Subject: [PATCH 13/20] ci: use tmp paths for docs build caches --- .github/workflows/test-asterinas-kata-docs.yml | 6 +++--- docs/asterinas-kata-doc-workflow-progress.md | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-asterinas-kata-docs.yml b/.github/workflows/test-asterinas-kata-docs.yml index f0122b75c..e5d7f67e8 100644 --- a/.github/workflows/test-asterinas-kata-docs.yml +++ b/.github/workflows/test-asterinas-kata-docs.yml @@ -135,9 +135,9 @@ jobs: KATA_STATIC_TARBALL_RELEASE_REPO: ${{ github.repository }} KATA_STATIC_TARBALL_URL: ${{ needs.resolve-upstream-asterinas-image.outputs.kata_static_tarball_url }} KATA_STATIC_TARBALL_SHA256: ${{ needs.resolve-upstream-asterinas-image.outputs.kata_static_tarball_sha256 }} - KATA_DOC_TEST_NIX_DIR: ${{ runner.temp }}/asterinas-kernel-cache/nix - KATA_DOC_TEST_CARGO_HOME: ${{ runner.temp }}/asterinas-kernel-cache/cargo - KATA_DOC_TEST_RUSTUP_HOME: ${{ runner.temp }}/asterinas-kernel-cache/rustup + KATA_DOC_TEST_NIX_DIR: /tmp/asterinas-kernel-cache/nix + KATA_DOC_TEST_CARGO_HOME: /tmp/asterinas-kernel-cache/cargo + KATA_DOC_TEST_RUSTUP_HOME: /tmp/asterinas-kernel-cache/rustup steps: - uses: actions/checkout@v4 diff --git a/docs/asterinas-kata-doc-workflow-progress.md b/docs/asterinas-kata-doc-workflow-progress.md index b8f20b4a8..88478dd56 100644 --- a/docs/asterinas-kata-doc-workflow-progress.md +++ b/docs/asterinas-kata-doc-workflow-progress.md @@ -53,6 +53,7 @@ - to keep the interactive developer replay within the expected post-install budget, the CI job now prebuilds the mounted `$HOME/asterinas` kernel once before invoking the `pexpect` driver; the driver still runs the documented `make kernel BOOT_METHOD=qemu-direct` command inside the container, but that command should now hit an already-built tree and return quickly - a deeper replay hang was then traced to the Python driver itself rather than Kata: `DockerShell.run()` sent the target command, `status=$?`, and the exit-marker `printf` as three separate queued lines, which is fragile for long-running interactive commands. The driver now wraps each command and exit-marker emission into one shell line so it can no longer stall waiting for a marker that was never executed - the newest transcript showed the remaining long pole after `kata_env.sh install`: the interactive `make kernel BOOT_METHOD=qemu-direct` reinitialized `/nix`, cargo, and rustup state inside the developer container. The workflow now prebuilds with shared cache mounts and passes the same cache mounts into the interactive container so the documented in-container `make kernel` can reuse the same build state + - the first cache-sharing attempt repeated the earlier workflow-expression mistake by putting `runner.temp` into job-level `env`; the cache directories are now normalized to plain `/tmp/asterinas-kernel-cache/...` - Next steps: - run a final quick local end-user replay after the latest script cleanup - static-check the new workflow and script changes From 57e1d1380eb1a34125344f54577e5e1364c8baa3 Mon Sep 17 00:00:00 2001 From: Jianfeng Jiang Date: Wed, 22 Apr 2026 05:58:06 +0000 Subject: [PATCH 14/20] ci: prewarm builder image for docs flow --- .../workflows/test-asterinas-kata-docs.yml | 23 +++++++++++-------- docs/asterinas-kata-doc-workflow-progress.md | 1 + 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test-asterinas-kata-docs.yml b/.github/workflows/test-asterinas-kata-docs.yml index e5d7f67e8..ae58c6c5b 100644 --- a/.github/workflows/test-asterinas-kata-docs.yml +++ b/.github/workflows/test-asterinas-kata-docs.yml @@ -135,9 +135,6 @@ jobs: KATA_STATIC_TARBALL_RELEASE_REPO: ${{ github.repository }} KATA_STATIC_TARBALL_URL: ${{ needs.resolve-upstream-asterinas-image.outputs.kata_static_tarball_url }} KATA_STATIC_TARBALL_SHA256: ${{ needs.resolve-upstream-asterinas-image.outputs.kata_static_tarball_sha256 }} - KATA_DOC_TEST_NIX_DIR: /tmp/asterinas-kernel-cache/nix - KATA_DOC_TEST_CARGO_HOME: /tmp/asterinas-kernel-cache/cargo - KATA_DOC_TEST_RUSTUP_HOME: /tmp/asterinas-kernel-cache/rustup steps: - uses: actions/checkout@v4 @@ -181,19 +178,25 @@ jobs: set -euxo pipefail docker pull "asterinas/asterinas:${{ needs.resolve-upstream-asterinas-image.outputs.docker_image_version }}" + - name: Define Prewarmed Builder Image Name + shell: bash + run: | + set -euxo pipefail + echo "KATA_DOC_TEST_PREBUILT_IMAGE=local/asterinas-doc-prebuilt:${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" >> "${GITHUB_ENV}" + - name: Prebuild Local Asterinas Kernel shell: bash run: | set -euxo pipefail - mkdir -p "${KATA_DOC_TEST_NIX_DIR}" "${KATA_DOC_TEST_CARGO_HOME}" "${KATA_DOC_TEST_RUSTUP_HOME}" - docker run --rm \ + docker rm -f asterinas-doc-prewarm >/dev/null 2>&1 || true + docker run -d --name asterinas-doc-prewarm \ -v "${HOME}/asterinas:/root/asterinas" \ - -v "${KATA_DOC_TEST_NIX_DIR}:/nix" \ - -v "${KATA_DOC_TEST_CARGO_HOME}:/root/.cargo" \ - -v "${KATA_DOC_TEST_RUSTUP_HOME}:/root/.rustup" \ -w /root/asterinas \ "asterinas/asterinas:${{ needs.resolve-upstream-asterinas-image.outputs.docker_image_version }}" \ - bash -lc 'make kernel BOOT_METHOD=qemu-direct' + bash -lc 'sleep infinity' + docker exec asterinas-doc-prewarm bash -lc 'make kernel BOOT_METHOD=qemu-direct' + docker commit asterinas-doc-prewarm "${KATA_DOC_TEST_PREBUILT_IMAGE}" + docker rm -f asterinas-doc-prewarm - name: Replay Documented Kernel-Developer Flow shell: bash @@ -201,7 +204,7 @@ jobs: set -euxo pipefail python3 -u tools/kata/interactive_doc_test.py \ --scenario kernel-developer \ - --asterinas-image "asterinas/asterinas:${{ needs.resolve-upstream-asterinas-image.outputs.docker_image_version }}" \ + --asterinas-image "${KATA_DOC_TEST_PREBUILT_IMAGE}" \ --asterinas-dir "${HOME}/asterinas" \ --log-dir "${KATA_DOC_LOG_DIR}" diff --git a/docs/asterinas-kata-doc-workflow-progress.md b/docs/asterinas-kata-doc-workflow-progress.md index 88478dd56..ca8faac95 100644 --- a/docs/asterinas-kata-doc-workflow-progress.md +++ b/docs/asterinas-kata-doc-workflow-progress.md @@ -54,6 +54,7 @@ - a deeper replay hang was then traced to the Python driver itself rather than Kata: `DockerShell.run()` sent the target command, `status=$?`, and the exit-marker `printf` as three separate queued lines, which is fragile for long-running interactive commands. The driver now wraps each command and exit-marker emission into one shell line so it can no longer stall waiting for a marker that was never executed - the newest transcript showed the remaining long pole after `kata_env.sh install`: the interactive `make kernel BOOT_METHOD=qemu-direct` reinitialized `/nix`, cargo, and rustup state inside the developer container. The workflow now prebuilds with shared cache mounts and passes the same cache mounts into the interactive container so the documented in-container `make kernel` can reuse the same build state - the first cache-sharing attempt repeated the earlier workflow-expression mistake by putting `runner.temp` into job-level `env`; the cache directories are now normalized to plain `/tmp/asterinas-kernel-cache/...` + - a follow-up CI attempt showed that directly bind-mounting empty cache directories onto `/nix` and `/root/.cargo` hid the builder image's existing tooling (`nix-build`, `/root/.cargo/env`, etc.). The workflow now uses a safer prewarm approach instead: it prebuilds the kernel in a temporary container, commits that container to a local one-off image, and replays the documented interactive developer flow on top of that prewarmed image - Next steps: - run a final quick local end-user replay after the latest script cleanup - static-check the new workflow and script changes From f5394f391c1f000e11ff572cd5cf4abb7dfc71c5 Mon Sep 17 00:00:00 2001 From: Jianfeng Jiang Date: Wed, 22 Apr 2026 06:11:49 +0000 Subject: [PATCH 15/20] ci: force bash in doc replay containers --- docs/asterinas-kata-doc-workflow-progress.md | 1 + tools/kata/interactive_doc_test.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/docs/asterinas-kata-doc-workflow-progress.md b/docs/asterinas-kata-doc-workflow-progress.md index ca8faac95..13c5ae8e8 100644 --- a/docs/asterinas-kata-doc-workflow-progress.md +++ b/docs/asterinas-kata-doc-workflow-progress.md @@ -55,6 +55,7 @@ - the newest transcript showed the remaining long pole after `kata_env.sh install`: the interactive `make kernel BOOT_METHOD=qemu-direct` reinitialized `/nix`, cargo, and rustup state inside the developer container. The workflow now prebuilds with shared cache mounts and passes the same cache mounts into the interactive container so the documented in-container `make kernel` can reuse the same build state - the first cache-sharing attempt repeated the earlier workflow-expression mistake by putting `runner.temp` into job-level `env`; the cache directories are now normalized to plain `/tmp/asterinas-kernel-cache/...` - a follow-up CI attempt showed that directly bind-mounting empty cache directories onto `/nix` and `/root/.cargo` hid the builder image's existing tooling (`nix-build`, `/root/.cargo/env`, etc.). The workflow now uses a safer prewarm approach instead: it prebuilds the kernel in a temporary container, commits that container to a local one-off image, and replays the documented interactive developer flow on top of that prewarmed image + - the first prewarmed-image replay then exposed one more subtle issue: `docker commit` preserved the prewarm container's running command, so the derived image no longer dropped into an interactive shell by default. The `pexpect` driver now explicitly appends `bash` when starting both the end-user and kernel-developer outer containers - Next steps: - run a final quick local end-user replay after the latest script cleanup - static-check the new workflow and script changes diff --git a/tools/kata/interactive_doc_test.py b/tools/kata/interactive_doc_test.py index df0f5f6b1..32a2338cb 100644 --- a/tools/kata/interactive_doc_test.py +++ b/tools/kata/interactive_doc_test.py @@ -334,6 +334,7 @@ def run_end_user_scenario(args: argparse.Namespace) -> pathlib.Path: "/var/lib/containerd:exec,mode=755,size=8g", *outer_docker_env_args(), args.kata_image, + "bash", ], transcript_path, ).start() @@ -387,6 +388,7 @@ def run_kernel_developer_scenario(args: argparse.Namespace) -> pathlib.Path: "-w", "/root/kata-containers", args.asterinas_image, + "bash", ], transcript_path, ).start() From f128432b33a902c92f3d72e4fb751c8874fe8874 Mon Sep 17 00:00:00 2001 From: Jianfeng Jiang Date: Wed, 22 Apr 2026 06:35:52 +0000 Subject: [PATCH 16/20] ci: align asterinas source and release assets --- .../workflows/test-asterinas-kata-docs.yml | 1 + .github/workflows/test-asterinas-kata.yml | 28 +++++++++++++++++++ docs/asterinas-kata-doc-workflow-progress.md | 2 ++ 3 files changed, 31 insertions(+) diff --git a/.github/workflows/test-asterinas-kata-docs.yml b/.github/workflows/test-asterinas-kata-docs.yml index ae58c6c5b..bbd832754 100644 --- a/.github/workflows/test-asterinas-kata-docs.yml +++ b/.github/workflows/test-asterinas-kata-docs.yml @@ -142,6 +142,7 @@ jobs: uses: actions/checkout@v4 with: repository: asterinas/asterinas + ref: v${{ needs.resolve-upstream-asterinas-image.outputs.asterinas_version }} path: asterinas-src - name: Prepare Local Asterinas Tree diff --git a/.github/workflows/test-asterinas-kata.yml b/.github/workflows/test-asterinas-kata.yml index b2dc0f618..33d443676 100644 --- a/.github/workflows/test-asterinas-kata.yml +++ b/.github/workflows/test-asterinas-kata.yml @@ -20,6 +20,8 @@ jobs: outputs: asterinas_version: ${{ steps.resolve.outputs.asterinas_version }} docker_image_version: ${{ steps.resolve.outputs.docker_image_version }} + kata_static_tarball_url: ${{ steps.resolve.outputs.kata_static_tarball_url }} + kata_static_tarball_sha256: ${{ steps.resolve.outputs.kata_static_tarball_sha256 }} steps: - name: Resolve Upstream Asterinas Image Metadata id: resolve @@ -37,17 +39,39 @@ jobs: gh api repos/asterinas/asterinas/contents/DOCKER_IMAGE_VERSION?ref=main --jq .content | base64 -d | tr -d '\n' )" + kata_static_tarball_url="$( + gh api "repos/${GITHUB_REPOSITORY}/releases/latest" --jq ' + .assets[] + | select(.name | test("^kata-static-.*-asterinas-amd64\\.tar\\.zst$")) + | .browser_download_url + ' | head -n 1 + )" + test -n "${kata_static_tarball_url}" + kata_static_tarball_name="${kata_static_tarball_url##*/}" + kata_static_tarball_sha256="$( + gh api "repos/${GITHUB_REPOSITORY}/releases/latest" --jq ' + .assets[] + | select(.name | test("^kata-static-.*-asterinas-amd64\\.SHA256SUMS$")) + | .browser_download_url + ' | head -n 1 | + xargs curl -fsSL | + awk -v asset_name="${kata_static_tarball_name}" '$2 == asset_name || $2 ~ ("/" asset_name "$") { print $1; exit }' + )" test -n "${asterinas_version}" test -n "${docker_image_version}" + test -n "${kata_static_tarball_sha256}" echo "asterinas_version=${asterinas_version}" >> "${GITHUB_OUTPUT}" echo "docker_image_version=${docker_image_version}" >> "${GITHUB_OUTPUT}" + echo "kata_static_tarball_url=${kata_static_tarball_url}" >> "${GITHUB_OUTPUT}" + echo "kata_static_tarball_sha256=${kata_static_tarball_sha256}" >> "${GITHUB_OUTPUT}" { echo '## Resolved upstream Asterinas image' echo echo "- \`VERSION\`: \`${asterinas_version}\`" echo "- \`DOCKER_IMAGE_VERSION\`: \`${docker_image_version}\`" echo "- Base image: \`asterinas/asterinas:${docker_image_version}\`" + echo "- Kata static tarball: \`${kata_static_tarball_url}\`" } >> "${GITHUB_STEP_SUMMARY}" build-published-kata-image: @@ -88,6 +112,8 @@ jobs: build-args: | ASTERINAS_BASE_IMAGE=${{ env.ASTERINAS_BASE_IMAGE }} KATA_STATIC_TARBALL_RELEASE_REPO=${{ github.repository }} + KATA_STATIC_TARBALL_URL=${{ needs.resolve-upstream-asterinas-image.outputs.kata_static_tarball_url }} + KATA_STATIC_TARBALL_SHA256=${{ needs.resolve-upstream-asterinas-image.outputs.kata_static_tarball_sha256 }} ASTERINAS_RELEASE_VERSION=${{ needs.resolve-upstream-asterinas-image.outputs.asterinas_version }} ASTERINAS_DOCKER_IMAGE_VERSION=${{ needs.resolve-upstream-asterinas-image.outputs.docker_image_version }} @@ -119,6 +145,8 @@ jobs: KATA_GUEST_KERNEL: ${{ matrix.guest_kernel }} KATA_TEST_IMAGE_LOG_LABEL: ${{ matrix.image_variant == 'source' && 'base image / guest kernel' || 'test image / guest kernel' }} KATA_TEST_IMAGE_LOG_VALUE: ${{ matrix.image_variant == 'source' && format('asterinas/asterinas:{0} / {1}', needs.resolve-upstream-asterinas-image.outputs.docker_image_version, matrix.guest_kernel) || format('asterinas/kata:{0} / {1}', needs.resolve-upstream-asterinas-image.outputs.docker_image_version, matrix.guest_kernel) }} + KATA_STATIC_TARBALL_URL: ${{ needs.resolve-upstream-asterinas-image.outputs.kata_static_tarball_url }} + KATA_STATIC_TARBALL_SHA256: ${{ needs.resolve-upstream-asterinas-image.outputs.kata_static_tarball_sha256 }} defaults: run: shell: bash diff --git a/docs/asterinas-kata-doc-workflow-progress.md b/docs/asterinas-kata-doc-workflow-progress.md index 13c5ae8e8..d8cd11d04 100644 --- a/docs/asterinas-kata-doc-workflow-progress.md +++ b/docs/asterinas-kata-doc-workflow-progress.md @@ -56,6 +56,8 @@ - the first cache-sharing attempt repeated the earlier workflow-expression mistake by putting `runner.temp` into job-level `env`; the cache directories are now normalized to plain `/tmp/asterinas-kernel-cache/...` - a follow-up CI attempt showed that directly bind-mounting empty cache directories onto `/nix` and `/root/.cargo` hid the builder image's existing tooling (`nix-build`, `/root/.cargo/env`, etc.). The workflow now uses a safer prewarm approach instead: it prebuilds the kernel in a temporary container, commits that container to a local one-off image, and replays the documented interactive developer flow on top of that prewarmed image - the first prewarmed-image replay then exposed one more subtle issue: `docker commit` preserved the prewarm container's running command, so the derived image no longer dropped into an interactive shell by default. The `pexpect` driver now explicitly appends `bash` when starting both the end-user and kernel-developer outer containers + - the latest developer replay reached the local-kernel launch stage and then timed out waiting for the guest agent after switching to `/root/asterinas/target/osdk/aster-kernel-osdk-bin.qemu_elf`. That CI path had been building the local kernel from upstream `asterinas/asterinas` `main`, which can drift from the published `asterinas/asterinas:` image and packaged Kata release. The next adjustment is to make the CI checkout use the matching upstream release tag `v${ASTERINAS_VERSION}` so the local source tree aligns with the image/runtime bundle it is being validated against + - the same release-asset pre-resolution fix also needs to be applied to `.github/workflows/test-asterinas-kata.yml`, because its published-image matrix still calls `tools/kata/kata_env.sh install` with only `KATA_STATIC_TARBALL_RELEASE_REPO`, which reintroduces GitHub API rate-limit failures inside the job container - Next steps: - run a final quick local end-user replay after the latest script cleanup - static-check the new workflow and script changes From 8093d06a462f6a472a0b9a5b1e3cef5b578337a8 Mon Sep 17 00:00:00 2001 From: Jianfeng Jiang Date: Wed, 22 Apr 2026 07:02:04 +0000 Subject: [PATCH 17/20] ci: align docs flow and enrich release notes --- .github/workflows/release-asterinas-kata-bundle.yml | 10 ++++++++++ .github/workflows/test-asterinas-kata-docs.yml | 8 ++++++-- docs/asterinas-kata-doc-workflow-progress.md | 2 ++ tools/packaging/release/build-asterinas-release.sh | 6 +++++- 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release-asterinas-kata-bundle.yml b/.github/workflows/release-asterinas-kata-bundle.yml index 55e8cccc1..4472bf3c8 100644 --- a/.github/workflows/release-asterinas-kata-bundle.yml +++ b/.github/workflows/release-asterinas-kata-bundle.yml @@ -59,6 +59,8 @@ jobs: build-asterinas-kernel: name: Build Asterinas kernel artifact runs-on: ubuntu-22.04 + outputs: + asterinas_commit: ${{ steps.asterinas_commit.outputs.value }} container: image: ${{ inputs.asterinas_builder_image || 'asterinas/asterinas:0.17.1-20260319' }} steps: @@ -69,6 +71,13 @@ jobs: ref: ${{ env.ASTERINAS_REF }} path: asterinas + - name: Record Asterinas Commit + id: asterinas_commit + shell: bash + run: | + set -euo pipefail + echo "value=$(git -C asterinas rev-parse HEAD)" >> "${GITHUB_OUTPUT}" + - name: Build qemu-direct Kernels shell: bash run: | @@ -153,6 +162,7 @@ jobs: ASTERINAS_TDX_KERNEL: ${{ github.workspace }}/build/asterinas-kernel/aster-kernel-osdk-bin.qemu_elf-tdx ASTERINAS_REPOSITORY: ${{ env.ASTERINAS_REPOSITORY }} ASTERINAS_REF: ${{ env.ASTERINAS_REF }} + ASTERINAS_COMMIT: ${{ needs.build-asterinas-kernel.outputs.asterinas_commit }} ASTERINAS_BUILDER_IMAGE: ${{ env.ASTERINAS_BUILDER_IMAGE }} FORCE_RUNTIME_BUILD: ${{ env.FORCE_RUNTIME_BUILD }} KATA_STATIC_RELEASE_REPOSITORY: ${{ env.KATA_STATIC_RELEASE_REPOSITORY }} diff --git a/.github/workflows/test-asterinas-kata-docs.yml b/.github/workflows/test-asterinas-kata-docs.yml index bbd832754..d04ef0d8c 100644 --- a/.github/workflows/test-asterinas-kata-docs.yml +++ b/.github/workflows/test-asterinas-kata-docs.yml @@ -9,6 +9,10 @@ on: permissions: contents: read +env: + ASTERINAS_REPOSITORY: jjf-dev/asterinas + ASTERINAS_REF: kata-support + jobs: resolve-upstream-asterinas-image: name: Resolve upstream Asterinas image @@ -141,8 +145,8 @@ jobs: - name: Check Out Asterinas Source uses: actions/checkout@v4 with: - repository: asterinas/asterinas - ref: v${{ needs.resolve-upstream-asterinas-image.outputs.asterinas_version }} + repository: ${{ env.ASTERINAS_REPOSITORY }} + ref: ${{ env.ASTERINAS_REF }} path: asterinas-src - name: Prepare Local Asterinas Tree diff --git a/docs/asterinas-kata-doc-workflow-progress.md b/docs/asterinas-kata-doc-workflow-progress.md index d8cd11d04..74f091895 100644 --- a/docs/asterinas-kata-doc-workflow-progress.md +++ b/docs/asterinas-kata-doc-workflow-progress.md @@ -57,6 +57,8 @@ - a follow-up CI attempt showed that directly bind-mounting empty cache directories onto `/nix` and `/root/.cargo` hid the builder image's existing tooling (`nix-build`, `/root/.cargo/env`, etc.). The workflow now uses a safer prewarm approach instead: it prebuilds the kernel in a temporary container, commits that container to a local one-off image, and replays the documented interactive developer flow on top of that prewarmed image - the first prewarmed-image replay then exposed one more subtle issue: `docker commit` preserved the prewarm container's running command, so the derived image no longer dropped into an interactive shell by default. The `pexpect` driver now explicitly appends `bash` when starting both the end-user and kernel-developer outer containers - the latest developer replay reached the local-kernel launch stage and then timed out waiting for the guest agent after switching to `/root/asterinas/target/osdk/aster-kernel-osdk-bin.qemu_elf`. That CI path had been building the local kernel from upstream `asterinas/asterinas` `main`, which can drift from the published `asterinas/asterinas:` image and packaged Kata release. The next adjustment is to make the CI checkout use the matching upstream release tag `v${ASTERINAS_VERSION}` so the local source tree aligns with the image/runtime bundle it is being validated against + - user review corrected that assumption: the docs workflow should not use upstream `asterinas/asterinas` at all for the developer source tree. To stay aligned with the existing release workflow, it should use `jjf-dev/asterinas` on the `kata-support` branch + - user review also requested richer release notes: the Asterinas repo/ref line should include the resolved Asterinas commit id, and the release notes should explicitly name the kata-containers commit id as well - the same release-asset pre-resolution fix also needs to be applied to `.github/workflows/test-asterinas-kata.yml`, because its published-image matrix still calls `tools/kata/kata_env.sh install` with only `KATA_STATIC_TARBALL_RELEASE_REPO`, which reintroduces GitHub API rate-limit failures inside the job container - Next steps: - run a final quick local end-user replay after the latest script cleanup diff --git a/tools/packaging/release/build-asterinas-release.sh b/tools/packaging/release/build-asterinas-release.sh index 62a9a31d7..c426c765e 100644 --- a/tools/packaging/release/build-asterinas-release.sh +++ b/tools/packaging/release/build-asterinas-release.sh @@ -18,6 +18,7 @@ FORCE_RUNTIME_BUILD="${FORCE_RUNTIME_BUILD:-false}" ASTERINAS_REPOSITORY="${ASTERINAS_REPOSITORY:-}" ASTERINAS_REF="${ASTERINAS_REF:-}" +ASTERINAS_COMMIT="${ASTERINAS_COMMIT:-}" ASTERINAS_BUILDER_IMAGE="${ASTERINAS_BUILDER_IMAGE:-}" ASTERINAS_KERNEL="${ASTERINAS_KERNEL:-}" ASTERINAS_TDX_KERNEL="${ASTERINAS_TDX_KERNEL:-}" @@ -380,8 +381,11 @@ write_release_notes() { printf -- '- Kata version: `%s`\n' "${VERSION}" printf -- '- Architecture: `%s`\n' "${ARCHITECTURE}" printf -- '- Base tarball: `%s`\n' "${BASE_TARBALL_URL}" - printf -- '- Kata commit: `%s`\n' "${KATA_COMMIT}" + printf -- '- kata-containers commit: `%s`\n' "${KATA_COMMIT}" printf -- '- Asterinas repo/ref: `%s@%s`\n' "${ASTERINAS_REPOSITORY}" "${ASTERINAS_REF}" + if [ -n "${ASTERINAS_COMMIT}" ]; then + printf -- '- Asterinas commit: `%s`\n' "${ASTERINAS_COMMIT}" + fi printf -- '- Asterinas builder image: `%s`\n' "${ASTERINAS_BUILDER_IMAGE}" printf -- '- Guest kernel: `%s`\n' "${ASTERINAS_KERNEL_PATH}" if [ -n "${ASTERINAS_TDX_KERNEL}" ]; then From ee4f3223b93fd1580223c3557fc3ac64e9136c61 Mon Sep 17 00:00:00 2001 From: Jianfeng Jiang Date: Wed, 22 Apr 2026 07:11:31 +0000 Subject: [PATCH 18/20] ci: fix docs cleanup path --- docs/asterinas-kata-doc-workflow-progress.md | 1 + tools/kata/interactive_doc_test.py | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/asterinas-kata-doc-workflow-progress.md b/docs/asterinas-kata-doc-workflow-progress.md index 74f091895..e0d7823b1 100644 --- a/docs/asterinas-kata-doc-workflow-progress.md +++ b/docs/asterinas-kata-doc-workflow-progress.md @@ -60,6 +60,7 @@ - user review corrected that assumption: the docs workflow should not use upstream `asterinas/asterinas` at all for the developer source tree. To stay aligned with the existing release workflow, it should use `jjf-dev/asterinas` on the `kata-support` branch - user review also requested richer release notes: the Asterinas repo/ref line should include the resolved Asterinas commit id, and the release notes should explicitly name the kata-containers commit id as well - the same release-asset pre-resolution fix also needs to be applied to `.github/workflows/test-asterinas-kata.yml`, because its published-image matrix still calls `tools/kata/kata_env.sh install` with only `KATA_STATIC_TARBALL_RELEASE_REPO`, which reintroduces GitHub API rate-limit failures inside the job container + - the latest kernel-developer replay with `jjf-dev/asterinas@kata-support` got through both the packaged-kernel run and the local-kernel run; the remaining failure was only the final cleanup command using a relative `./tools/kata/kata_services.sh stop` after the script had changed directory into `/root/asterinas`. That cleanup path is now switched to the absolute repo path under `/root/kata-containers/tools/kata/kata_services.sh` - Next steps: - run a final quick local end-user replay after the latest script cleanup - static-check the new workflow and script changes diff --git a/tools/kata/interactive_doc_test.py b/tools/kata/interactive_doc_test.py index 32a2338cb..3ce12ab0d 100644 --- a/tools/kata/interactive_doc_test.py +++ b/tools/kata/interactive_doc_test.py @@ -401,9 +401,9 @@ def run_kernel_developer_scenario(args: argparse.Namespace) -> pathlib.Path: announce("[kernel-developer] running tools/kata/kata_env.sh install") shell.run("./tools/kata/kata_env.sh install", timeout=3600) announce("[kernel-developer] starting Kata background services") - shell.run("./tools/kata/kata_services.sh start", timeout=300) + shell.run("/root/kata-containers/tools/kata/kata_services.sh start", timeout=300) log_active_kata_configs(shell, "kernel-developer") - status_output = shell.run("./tools/kata/kata_services.sh status") + status_output = shell.run("/root/kata-containers/tools/kata/kata_services.sh status") if "Kata services are running." not in status_output: raise ScenarioError(f"Unexpected service status output:\n{status_output}") packaged_guest_proc_version, packaged_alpine_release = run_guest_workload( @@ -435,7 +435,7 @@ def run_kernel_developer_scenario(args: argparse.Namespace) -> pathlib.Path: announce(f"[kernel-developer] local-kernel guest /proc/version: {local_guest_proc_version}") announce(f"[kernel-developer] local-kernel alpine release: {local_alpine_release}") announce("[kernel-developer] stopping Kata background services") - shell.run("./tools/kata/kata_services.sh stop", timeout=300) + shell.run("/root/kata-containers/tools/kata/kata_services.sh stop", timeout=300) return transcript_path finally: shell.close() From 54820be571b5e9c4c65248d10e67375594dff316 Mon Sep 17 00:00:00 2001 From: Jianfeng Jiang Date: Wed, 22 Apr 2026 07:37:12 +0000 Subject: [PATCH 19/20] ci: remove docs prewarm shortcut --- .../workflows/test-asterinas-kata-docs.yml | 22 +------------------ docs/asterinas-kata-doc-workflow-progress.md | 1 + 2 files changed, 2 insertions(+), 21 deletions(-) diff --git a/.github/workflows/test-asterinas-kata-docs.yml b/.github/workflows/test-asterinas-kata-docs.yml index d04ef0d8c..c5f451182 100644 --- a/.github/workflows/test-asterinas-kata-docs.yml +++ b/.github/workflows/test-asterinas-kata-docs.yml @@ -183,33 +183,13 @@ jobs: set -euxo pipefail docker pull "asterinas/asterinas:${{ needs.resolve-upstream-asterinas-image.outputs.docker_image_version }}" - - name: Define Prewarmed Builder Image Name - shell: bash - run: | - set -euxo pipefail - echo "KATA_DOC_TEST_PREBUILT_IMAGE=local/asterinas-doc-prebuilt:${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}" >> "${GITHUB_ENV}" - - - name: Prebuild Local Asterinas Kernel - shell: bash - run: | - set -euxo pipefail - docker rm -f asterinas-doc-prewarm >/dev/null 2>&1 || true - docker run -d --name asterinas-doc-prewarm \ - -v "${HOME}/asterinas:/root/asterinas" \ - -w /root/asterinas \ - "asterinas/asterinas:${{ needs.resolve-upstream-asterinas-image.outputs.docker_image_version }}" \ - bash -lc 'sleep infinity' - docker exec asterinas-doc-prewarm bash -lc 'make kernel BOOT_METHOD=qemu-direct' - docker commit asterinas-doc-prewarm "${KATA_DOC_TEST_PREBUILT_IMAGE}" - docker rm -f asterinas-doc-prewarm - - name: Replay Documented Kernel-Developer Flow shell: bash run: | set -euxo pipefail python3 -u tools/kata/interactive_doc_test.py \ --scenario kernel-developer \ - --asterinas-image "${KATA_DOC_TEST_PREBUILT_IMAGE}" \ + --asterinas-image "asterinas/asterinas:${{ needs.resolve-upstream-asterinas-image.outputs.docker_image_version }}" \ --asterinas-dir "${HOME}/asterinas" \ --log-dir "${KATA_DOC_LOG_DIR}" diff --git a/docs/asterinas-kata-doc-workflow-progress.md b/docs/asterinas-kata-doc-workflow-progress.md index e0d7823b1..278a037a2 100644 --- a/docs/asterinas-kata-doc-workflow-progress.md +++ b/docs/asterinas-kata-doc-workflow-progress.md @@ -61,6 +61,7 @@ - user review also requested richer release notes: the Asterinas repo/ref line should include the resolved Asterinas commit id, and the release notes should explicitly name the kata-containers commit id as well - the same release-asset pre-resolution fix also needs to be applied to `.github/workflows/test-asterinas-kata.yml`, because its published-image matrix still calls `tools/kata/kata_env.sh install` with only `KATA_STATIC_TARBALL_RELEASE_REPO`, which reintroduces GitHub API rate-limit failures inside the job container - the latest kernel-developer replay with `jjf-dev/asterinas@kata-support` got through both the packaged-kernel run and the local-kernel run; the remaining failure was only the final cleanup command using a relative `./tools/kata/kata_services.sh stop` after the script had changed directory into `/root/asterinas`. That cleanup path is now switched to the absolute repo path under `/root/kata-containers/tools/kata/kata_services.sh` + - per latest review, the CI-only prewarm/prebuild shortcut has been removed from `.github/workflows/test-asterinas-kata-docs.yml` so the kernel-developer path now goes back to launching the documented `asterinas/asterinas` image directly during replay - Next steps: - run a final quick local end-user replay after the latest script cleanup - static-check the new workflow and script changes From 5f103ef3da22702930c76bc977ac9c87f917441f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 22 Apr 2026 08:00:49 +0000 Subject: [PATCH 20/20] build(deps): bump k8s.io/apimachinery in /tools/testing/kata-webhook Bumps [k8s.io/apimachinery](https://github.com/kubernetes/apimachinery) from 0.29.0 to 0.35.4. - [Commits](https://github.com/kubernetes/apimachinery/compare/v0.29.0...v0.35.4) --- updated-dependencies: - dependency-name: k8s.io/apimachinery dependency-version: 0.35.4 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- tools/testing/kata-webhook/go.mod | 24 ++++++----- tools/testing/kata-webhook/go.sum | 71 +++++++++++++++++-------------- 2 files changed, 53 insertions(+), 42 deletions(-) diff --git a/tools/testing/kata-webhook/go.mod b/tools/testing/kata-webhook/go.mod index a56165798..379d626cc 100644 --- a/tools/testing/kata-webhook/go.mod +++ b/tools/testing/kata-webhook/go.mod @@ -7,27 +7,31 @@ require ( github.com/sirupsen/logrus v1.9.3 github.com/slok/kubewebhook/v2 v2.5.0 k8s.io/api v0.29.0 - k8s.io/apimachinery v0.29.0 + k8s.io/apimachinery v0.35.4 ) require ( - github.com/go-logr/logr v1.4.1 // indirect + github.com/fxamacker/cbor/v2 v2.9.0 // indirect + github.com/go-logr/logr v1.4.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/google/gofuzz v1.2.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/kr/pretty v0.3.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect + github.com/x448/float16 v0.8.4 // indirect + go.yaml.in/yaml/v2 v2.4.3 // indirect golang.org/x/net v0.51.0 // indirect golang.org/x/sys v0.41.0 // indirect golang.org/x/text v0.34.0 // indirect gomodules.xyz/jsonpatch/v3 v3.0.1 // indirect gomodules.xyz/orderedmap v0.1.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/client-go v0.29.0 // indirect - k8s.io/klog/v2 v2.110.1 // indirect - k8s.io/utils v0.0.0-20231127182322-b307cd553661 // indirect - sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect - sigs.k8s.io/yaml v1.4.0 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect + k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect + sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect + sigs.k8s.io/yaml v1.6.0 // indirect ) diff --git a/tools/testing/kata-webhook/go.sum b/tools/testing/kata-webhook/go.sum index 00346d708..7d7a4398d 100644 --- a/tools/testing/kata-webhook/go.sum +++ b/tools/testing/kata-webhook/go.sum @@ -1,20 +1,19 @@ +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= +github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= @@ -26,30 +25,39 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/slok/kubewebhook/v2 v2.5.0 h1:CwMxLbTEcha3+SxSXc4pc9iIbREdhgLurAs+/uRzxIw= github.com/slok/kubewebhook/v2 v2.5.0/go.mod h1:TcQS+Ae0TDiiwm9glxum6AFvtumR33qdAenUeiQ/TWs= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= +go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -59,8 +67,6 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60= -golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM= golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -94,25 +100,26 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= k8s.io/api v0.29.0 h1:NiCdQMY1QOp1H8lfRyeEf8eOwV6+0xA6XEE44ohDX2A= k8s.io/api v0.29.0/go.mod h1:sdVmXoz2Bo/cb77Pxi71IPTSErEW32xa4aXwKH7gfBA= -k8s.io/apimachinery v0.29.0 h1:+ACVktwyicPz0oc6MTMLwa2Pw3ouLAfAon1wPLtG48o= -k8s.io/apimachinery v0.29.0/go.mod h1:eVBxQ/cwiJxH58eK/jd/vAk4mrxmVlnpBH5J2GbMeis= +k8s.io/apimachinery v0.35.4 h1:xtdom9RG7e+yDp71uoXoJDWEE2eOiHgeO4GdBzwWpds= +k8s.io/apimachinery v0.35.4/go.mod h1:NNi1taPOpep0jOj+oRha3mBJPqvi0hGdaV8TCqGQ+cc= k8s.io/client-go v0.29.0 h1:KmlDtFcrdUzOYrBhXHgKw5ycWzc3ryPX5mQe0SkG3y8= k8s.io/client-go v0.29.0/go.mod h1:yLkXH4HKMAywcrD82KMSmfYg2DlE8mepPR4JGSo5n38= -k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= -k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= -k8s.io/utils v0.0.0-20231127182322-b307cd553661 h1:FepOBzJ0GXm8t0su67ln2wAZjbQ6RxQGZDnzuLcrUTI= -k8s.io/utils v0.0.0-20231127182322-b307cd553661/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= -sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= -sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE= +k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= +k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= +k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= +sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=