Files
kata-containers/tools/osbuilder/rootfs-builder/devkit/devkit-init.sh
Fabiano Fidêncio d3f3714edd EXPERIMENT: devkit - switch Alpine base to Ubuntu (noble)
Build the devkit debug extension from the same Ubuntu release the guest image
uses (assets.image ... version, e.g. noble) instead of Alpine. The rootfs is
assembled via 'docker build' + 'docker export' so apt resolves dependencies
natively, and the guest helper scripts wrap apt-get (devkit-apt) instead of apk.

Not for merge; experiment on branch experiment/devkit-ubuntu.
2026-07-23 16:50:36 +02:00

117 lines
4.2 KiB
Bash

#!/run/kata-extensions/devkit/bin/busybox.static sh
# shellcheck shell=dash
#
# Copyright (c) Kata Containers Community
#
# SPDX-License-Identifier: Apache-2.0
#
# Shared devkit guest runtime library, sourced by devkit-enter and devkit-apt.
#
# The devkit extension (read-only at ${DEVKIT}) is a full minimal Ubuntu rootfs.
# We overlay a writable, exec-enabled tmpfs on it and chroot in, so apt and the
# prebaked tools run natively against a normal root filesystem.
#
# BB is a statically-linked busybox (busybox-static, /bin/busybox.static), the
# ONLY binary we can exec on the shell-less guest base before the glibc dynamic
# loader is reachable there. It is installed at a dedicated path so it never
# clobbers the rootfs's own /bin/busybox or GNU coreutils.
DEVKIT_INIT_VERSION=1
DEVKIT="${DEVKIT:-/run/kata-extensions/devkit}"
WRITABLE="${WRITABLE:-/run/kata-devkit-writable}"
BB="${BB:-${DEVKIT}/bin/busybox.static}"
UPPER="${WRITABLE}/upper"
WORK="${WRITABLE}/work"
MERGED="${WRITABLE}/merged"
devkit_is_mounted() {
"${BB}" grep -q "[[:space:]]${1}[[:space:]]" /proc/mounts 2>/dev/null
}
# Mount an exec-enabled tmpfs at ${WRITABLE}: /run is typically noexec, so the
# overlay upper/work (and the fallback rootfs copy) must live on our own tmpfs
# for the prebaked and apt-installed binaries to exec.
devkit_mount_writable() {
"${BB}" mkdir -p "${WRITABLE}"
devkit_is_mounted "${WRITABLE}" && return 0
"${BB}" mount -t tmpfs -o mode=755,size=2G tmpfs "${WRITABLE}"
}
# Overlay (devkit ro lower + tmpfs rw upper), falling back to a plain copy of the
# rootfs into the tmpfs if this kernel refuses an overlay mount.
devkit_mount_root() {
"${BB}" mkdir -p "${UPPER}" "${WORK}" "${MERGED}"
devkit_is_mounted "${MERGED}" && return 0
"${BB}" test -f "${WRITABLE}/.copied" && return 0
local ovl_err
if ovl_err="$("${BB}" mount -t overlay overlay "${MERGED}" \
-o "lowerdir=${DEVKIT},upperdir=${UPPER},workdir=${WORK}" 2>&1)" \
&& devkit_is_mounted "${MERGED}"; then
return 0
fi
# Fallback: overlay unavailable on this kernel - copy the rootfs into tmpfs.
"${BB}" echo "devkit: overlay mount unavailable (${ovl_err:-unknown}), copying rootfs into tmpfs" >&2
"${BB}" cp -a "${DEVKIT}/." "${MERGED}/"
"${BB}" touch "${WRITABLE}/.copied"
}
# Bind the kernel virtual filesystems so apt and the debug tools behave; /dev is
# rbind'd to bring in pts/shm for interactive shells.
devkit_bind_mounts() {
"${BB}" mkdir -p "${MERGED}/proc" "${MERGED}/sys" "${MERGED}/dev"
devkit_is_mounted "${MERGED}/proc" || "${BB}" mount -t proc proc "${MERGED}/proc"
devkit_is_mounted "${MERGED}/sys" || "${BB}" mount -t sysfs sysfs "${MERGED}/sys"
if ! devkit_is_mounted "${MERGED}/dev"; then
if ! "${BB}" mount -o rbind /dev "${MERGED}/dev" 2>/dev/null; then
"${BB}" mount -t devtmpfs devtmpfs "${MERGED}/dev" 2>/dev/null \
|| "${BB}" mount -t tmpfs tmpfs "${MERGED}/dev"
"${BB}" mkdir -p "${MERGED}/dev/pts"
"${BB}" mount -t devpts devpts "${MERGED}/dev/pts" 2>/dev/null || true
fi
fi
}
# Give apt/curl working DNS by importing the guest resolver config.
devkit_seed_resolv_conf() {
"${BB}" test -e /etc/resolv.conf || return 0
"${BB}" mkdir -p "${MERGED}/etc"
"${BB}" cp -L /etc/resolv.conf "${MERGED}/etc/resolv.conf" 2>/dev/null || true
}
# Expose the guest's real root as /real_root so container rootfses and other
# guest state are reachable from the debug shell (e.g.
# /real_root/run/kata-containers/<id>/rootfs).
#
# A symlink to /proc/1/root, not a bind mount: proc is mounted in the chroot, so
# the kernel resolves it to PID 1's root regardless of the chroot, with no
# bind-mount recursion or teardown ordering to worry about.
devkit_link_real_root() {
"${BB}" test -e "${MERGED}/real_root" && return 0
"${BB}" ln -s /proc/1/root "${MERGED}/real_root" 2>/dev/null || true
}
# Idempotent: safe to call from every devkit-* invocation.
devkit_setup_chroot() {
devkit_mount_writable || return 1
devkit_mount_root || return 1
devkit_bind_mounts || return 1
devkit_seed_resolv_conf
devkit_link_real_root
"${BB}" echo "${DEVKIT_INIT_VERSION}" > "${WRITABLE}/.initialized" 2>/dev/null || true
return 0
}
devkit_chroot_exec() {
devkit_setup_chroot || {
"${BB}" echo "devkit: failed to set up chroot environment" >&2
return 1
}
exec "${BB}" chroot "${MERGED}" "$@"
}