name: Update kata-containers on the runner
on:
workflow_dispatch:
inputs:
kata-tag:
description: "kata-containers release tag (e.g. 3.20.0). Leave empty for the latest release."
required: false
type: string
default: ""
kata-ref:
description: "Arbitrary kata ref (branch/SHA) from the canonical kata repo. Overrides kata-tag; skips the kata-static release install."
required: false
type: string
default: ""
permissions:
contents: read
concurrency:
group: update-kata-${{ github.ref }}
cancel-in-progress: false
env:
KATA_REPO: kata-containers/kata-containers
KATA_SRC_DIR: /home/container-device-interface-rs/actions-runner/_work/kata-containers
KATA_INSTALL_PREFIX: /opt/kata
ROOTFS_IMAGE_DIR: /home/container-device-interface-rs/actions-runner/_work/kata-containers/build/rootfs-image-nvidia-gpu/builddir/rootfs-image/ubuntu_rootfs
IMAGE_BUILDER_DIR: /home/container-device-interface-rs/actions-runner/_work/kata-containers/tools/osbuilder/image-builder
LOCAL_BUILD_DIR: tools/packaging/kata-deploy/local-build/build
KBUILD_SIGN_PIN: cdi-rs-tests
jobs:
update:
runs-on: g5g.metal
steps:
- name: Ensure GitHub CLI is installed
run: |
set -euo pipefail
if command -v gh >/dev/null 2>&1; then
echo "gh already present: $(gh --version | head -n 1)"
exit 0
fi
echo "gh not found, installing from the official apt repository..."
export DEBIAN_FRONTEND=noninteractive
if ! command -v curl >/dev/null 2>&1; then
sudo -E apt-get update
sudo -E apt-get install -y curl
fi
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
| sudo tee /etc/apt/keyrings/githubcli-archive-keyring.gpg >/dev/null
sudo chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
| sudo tee /etc/apt/sources.list.d/github-cli.list >/dev/null
sudo -E apt-get update
sudo -E apt-get install -y gh
echo "Installed $(gh --version | head -n 1)"
- name: Resolve release tag
id: tag
if: ${{ inputs.kata-ref == '' }}
env:
GH_TOKEN: ${{ github.token }}
REQUESTED_TAG: ${{ inputs.kata-tag }}
run: |
set -euo pipefail
# `gh release view <tag>` maps to GET /repos/${KATA_REPO}/releases/tags/<tag>
# and `gh release view` (no tag) to .../releases/latest. An empty or
# "latest" request resolves the latest release. Capture stderr so a
# transient API/network failure surfaces instead of being silently
# relabelled "release not found" (the release may well exist).
want="${REQUESTED_TAG}"
[[ "${want}" == "latest" ]] && want=""
err=$(mktemp)
if ! tag=$(gh release view ${want:+"${want}"} \
--repo "${KATA_REPO}" \
--json tagName --jq '.tagName' 2>"${err}"); then
echo "ERROR: 'gh release view ${want:-<latest>}' failed for ${KATA_REPO}:" >&2
cat "${err}" >&2
exit 1
fi
if [[ -z "${tag}" ]]; then
echo "ERROR: could not resolve a tag for '${REQUESTED_TAG:-latest}' in ${KATA_REPO}" >&2
exit 1
fi
echo "Resolved '${REQUESTED_TAG:-latest}' -> ${tag}"
echo "tag=${tag}" >> "${GITHUB_OUTPUT}"
- name: Sync kata-containers source tree to ${{ inputs.kata-ref || steps.tag.outputs.tag }}
env:
TAG: ${{ steps.tag.outputs.tag }}
REF: ${{ inputs.kata-ref }}
run: |
set -euo pipefail
parent=$(dirname "${KATA_SRC_DIR}")
if [[ ! -d "${parent}" ]]; then
sudo mkdir -p "${parent}"
sudo chown "$(id -u)":"$(id -g)" "${parent}"
fi
if [[ ! -d "${KATA_SRC_DIR}/.git" ]]; then
echo "No source tree at ${KATA_SRC_DIR}, cloning ${KATA_REPO}..."
# Empty/stale dir would make git clone fail; clear it first.
if [[ -e "${KATA_SRC_DIR}" ]]; then
sudo rm -rf "${KATA_SRC_DIR}"
fi
git clone "https://github.com/${KATA_REPO}.git" "${KATA_SRC_DIR}"
fi
# Normalize ownership BEFORE any git operation: docker builds and
# earlier sudo git runs leave root-owned files (including inside
# .git), which makes git refuse the repo with "dubious ownership"
# and breaks ci.yaml's plain-git manifest reset. Owning the whole
# tree as the runner user fixes the root cause, so neither this
# workflow nor ci.yaml needs sudo git or safe.directory hacks.
sudo chown -R "$(id -u)":"$(id -g)" "${KATA_SRC_DIR}"
cd "${KATA_SRC_DIR}"
# Drop any local mutations from a previous rootfs build before
# moving the tree to a new tag. The build/ tree is regenerated
# by `make rootfs-image-nvidia-gpu-tarball` below.
git reset --hard
git clean -ffdx
if [[ -n "${REF}" ]]; then
# Canonical repo only - fork code never reaches the runner.
git fetch -- "https://github.com/${KATA_REPO}.git" "${REF}"
git checkout --detach FETCH_HEAD
else
git fetch --tags --force --prune origin
git checkout --detach "refs/tags/${TAG}"
fi
echo "kata-containers @ $(git rev-parse HEAD) ($(git describe --tags --always))"
- name: Rebuild rootfs-image-nvidia-gpu (signed nvidia driver)
run: |
set -euo pipefail
cd "${KATA_SRC_DIR}"
build_dir="${KATA_SRC_DIR}/${LOCAL_BUILD_DIR}"
kernel_tb="kata-static-kernel-nvidia-gpu.tar.zst"
# Build the kernel and rootfs in ONE invocation every run so both
# signing stages share a single key. rootfs-image-nvidia-gpu-tarball
# DEPS on kernel-nvidia-gpu-tarball: the kernel build generates the
# ephemeral signing key, embeds its public half in the vmlinuz and
# ships the KBUILD_SIGN_PIN-encrypted private key in the kernel
# headers, then the rootfs build (nvidia_chroot.sh) decrypts it with
# the same pin to sign nvidia.ko.
#
# The kernel is deliberately NOT cached across runs: a restored kernel
# carries an old public key while this run always re-signs nvidia.ko
# with a freshly generated key, so the guest kernel rejects the
# module ("Loading of module with unavailable key is rejected"). A
# fresh matched build is the whole point.
#
# Build as the runner user that owns the checkout (the kata-deploy
# build runs git in a container whose uid is derived from ${USER};
# building as root via sudo trips git's "dubious ownership" guard).
# script -fec keeps a TTY so docker output streams cleanly.
export USER="${USER:-$(id -un)}"
script -fec "USE_DOCKER=true KBUILD_SIGN_PIN=${KBUILD_SIGN_PIN} make rootfs-image-nvidia-gpu-tarball"
if [[ ! -d "${ROOTFS_IMAGE_DIR}" ]]; then
echo "ERROR: expected ${ROOTFS_IMAGE_DIR} after build, not found" >&2
exit 1
fi
if [[ ! -x "${IMAGE_BUILDER_DIR}/image_builder.sh" ]]; then
echo "ERROR: ${IMAGE_BUILDER_DIR}/image_builder.sh missing" >&2
exit 1
fi
if [[ ! -f "${build_dir}/${kernel_tb}" ]]; then
echo "ERROR: signed kernel tarball ${kernel_tb} not produced by the build" >&2
ls -l "${build_dir}" >&2 || true
exit 1
fi
# The containerized build leaves root-owned artifacts behind; own
# them now so the next run's git clean and ci.yaml's plain git both
# keep working without sudo.
sudo chown -R "$(id -u)":"$(id -g)" "${KATA_SRC_DIR}"
echo "rootfs ready at ${ROOTFS_IMAGE_DIR} (signed nvidia modules)"
- name: Download kata-static asset
id: download
if: ${{ inputs.kata-ref == '' }}
env:
GH_TOKEN: ${{ github.token }}
TAG: ${{ steps.tag.outputs.tag }}
run: |
set -euo pipefail
workdir=$(mktemp -d)
echo "workdir=${workdir}" >> "${GITHUB_OUTPUT}"
cd "${workdir}"
gh release download "${TAG}" \
--repo "${KATA_REPO}" \
--pattern 'kata-static-*-arm64.tar.zst'
asset=$(find . -maxdepth 1 -name 'kata-static-*-arm64.tar.zst' -printf '%f\n' 2>/dev/null | head -n 1 || true)
if [[ -z "${asset}" ]]; then
echo "ERROR: no kata-static arm64 asset in release ${TAG}" >&2
exit 1
fi
echo "Downloaded ${asset} ($(du -h "${asset}" | cut -f1))"
echo "asset=${workdir}/${asset}" >> "${GITHUB_OUTPUT}"
- name: Install kata-static to /opt/kata
if: ${{ inputs.kata-ref == '' }}
env:
TAG: ${{ steps.tag.outputs.tag }}
ASSET: ${{ steps.download.outputs.asset }}
run: |
set -euo pipefail
if [[ -d "${KATA_INSTALL_PREFIX}" ]]; then
stamp=$(date -u +%Y%m%dT%H%M%SZ)
backup="${KATA_INSTALL_PREFIX}.bak.${stamp}"
sudo mv "${KATA_INSTALL_PREFIX}" "${backup}"
echo "Previous install backed up to ${backup}"
fi
# kata-static tarballs are zstd-compressed and lay down files at
# /opt/kata when extracted from /.
sudo tar --zstd -xf "${ASSET}" -C /
if [[ ! -x "${KATA_INSTALL_PREFIX}/bin/containerd-shim-kata-v2" ]]; then
echo "ERROR: containerd-shim-kata-v2 not found after install" >&2
exit 1
fi
echo "Installed kata-static ${TAG} to ${KATA_INSTALL_PREFIX}"
- name: Overlay locally-built signed nvidia-gpu kernel
run: |
set -euo pipefail
# The released kernel enforces module signatures against kata's
# release key, but our nvidia modules are signed with our own
# ephemeral key. Boot OUR kernel so the signatures match. This must
# run AFTER the release kata-static install above, which would
# otherwise clobber the kernel. The kernel tarball lays files down
# under /opt/kata when extracted from /.
kernel_tb="${KATA_SRC_DIR}/${LOCAL_BUILD_DIR}/kata-static-kernel-nvidia-gpu.tar.zst"
if [[ ! -f "${kernel_tb}" ]]; then
echo "ERROR: ${kernel_tb} missing; nvidia-gpu kernel not built" >&2
exit 1
fi
sudo tar --zstd -xf "${kernel_tb}" -C /
kernel="${KATA_INSTALL_PREFIX}/share/kata-containers/vmlinuz-nvidia-gpu.container"
if [[ ! -e "${kernel}" ]]; then
echo "ERROR: ${kernel} not present after overlay" >&2
exit 1
fi
echo "Installed locally-built signed kernel: $(readlink -f "${kernel}")"
- name: Refresh /usr/local/bin symlinks
run: |
set -euo pipefail
# nerdctl resolves containerd-shim-kata-v2 via $PATH; the runner keeps
# a symlink at /usr/local/bin pointing into /opt/kata. Re-create it
# idempotently so a broken/missing link can't silently break the CI.
sudo ln -sfn "${KATA_INSTALL_PREFIX}/bin/containerd-shim-kata-v2" \
/usr/local/bin/containerd-shim-kata-v2
target=$(readlink -f /usr/local/bin/containerd-shim-kata-v2 || true)
if [[ "${target}" != "${KATA_INSTALL_PREFIX}/bin/containerd-shim-kata-v2" ]]; then
echo "ERROR: /usr/local/bin/containerd-shim-kata-v2 resolves to '${target}'" >&2
exit 1
fi
ls -l /usr/local/bin/containerd-shim-kata-v2
- name: Cleanup downloaded asset
if: always() && steps.download.outputs.workdir != ''
env:
WORKDIR: ${{ steps.download.outputs.workdir }}
run: rm -rf "${WORKDIR}"
- name: Summary
env:
TAG: ${{ inputs.kata-ref || steps.tag.outputs.tag }}
run: |
set -euo pipefail
src_head=$(git -C "${KATA_SRC_DIR}" rev-parse --short HEAD)
shim_version=$("${KATA_INSTALL_PREFIX}/bin/containerd-shim-kata-v2" --version 2>/dev/null | head -n 1 || echo "unknown")
{
echo "## kata-containers updated to \`${TAG}\`"
echo ""
echo "| item | value |"
echo "| --- | --- |"
echo "| source tree | \`${KATA_SRC_DIR}\` @ \`${src_head}\` |"
echo "| install prefix | \`${KATA_INSTALL_PREFIX}\` |"
echo "| shim version | \`${shim_version}\` |"
echo "| rootfs dir | \`${ROOTFS_IMAGE_DIR}\` |"
echo "| image builder | \`${IMAGE_BUILDER_DIR}\` |"
echo ""
echo "**Note:** \`/etc/kata-containers/configuration.toml\` was not modified."
echo "Diff against \`${KATA_INSTALL_PREFIX}/share/defaults/kata-containers/\` if upstream defaults moved."
} >> "${GITHUB_STEP_SUMMARY}"