kubestronaut-sim/images/vm/rootfs/usr/local/bin/ksim-vm
vumas3r f3d0e536b6 Exam simulator for CKA, CKAD and CKS with per-task hosts and a remote desktop
kubestronaut-sim runs each exam task on a host of its own - a kind (kubeadm)
cluster in rootless podman, or a QEMU/KVM Ubuntu VM for the CKS tasks that
need their own kernel (Falco, AppArmor) - and puts a browser desktop in front
of them, the way killer.sh and sailor.sh do.

- ksim, sim/: the engine (sessions, drills, study mode, grading, coverage,
  audit), host and VM lifecycle, the desktop
- images/: exam host images (common, CKS variant with its tools, VM guest)
- cka/ (131 tasks), ckad/ (105), cks/ (110): task banks, curricula with
  study notes, reference solutions and scripted solutions

Every domain of every track has at least 17 pool tasks. `./ksim audit` proves
a task three ways: it seeds cleanly on its own host, scores nothing before
anyone has worked on it, and reaches full marks when its scripted solution is
run on the host as `candidate`. All 346 tasks pass (CKA audited 2026-10-13,
CKAD and CKS 2026-10-12).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JZPd3GGWyxTgAkL6a4gAJz
2026-10-13 16:29:33 -06:00

45 lines
2.7 KiB
Bash

#!/bin/bash
# ksim-vm run boot the exam host of this container. Its disk is /env/disk.qcow2, a thin copy of
# the base disk made on the first start; /course is the task's /opt/course.
# ksim-vm build make the base disk: boot the Ubuntu cloud image once with guest/provision.sh as
# its first-boot script, and keep the result.
#
# /base .state/vm of the simulator: the Ubuntu cloud image and the base disks (read-only for run)
# /env the host's own directory (.state/envs/<host>): disk and console log
# /keys the simulator's ssh key pair (read-only)
set -euo pipefail
KVM=(-enable-kvm -cpu host -machine q35 -display none -monitor none -device virtio-rng-pci)
case "${1:-run}" in
run)
base=/base/${KSIM_BASE:?}
[ -f /env/disk.qcow2 ] || qemu-img create -q -f qcow2 -F qcow2 -b "$base" /env/disk.qcow2
# what the machine needs to know about itself, handed over as a second shared directory:
# its name, and the key the simulator and the desktop log in with (guest/firstboot.sh reads both)
mkdir -p /run/ksim && echo "${KSIM_HOSTNAME:?}" > /run/ksim/hostname && cp /keys/id_ed25519.pub /run/ksim/authorized_keys
exec qemu-system-x86_64 "${KVM[@]}" -smp "${KSIM_VM_CPUS:-2}" -m "${KSIM_VM_MB:-2048}" -serial file:/env/console.log \
-drive file=/env/disk.qcow2,if=virtio,cache=unsafe,discard=unmap \
-netdev user,id=n0,hostfwd=tcp::22-:22,hostfwd=tcp::6443-:6443 -device virtio-net-pci,netdev=n0 \
-virtfs local,path=/course,mount_tag=course,security_model=passthrough,id=course \
-virtfs local,path=/run/ksim,mount_tag=ksim,security_model=none,readonly=on,id=ksim
;;
build)
out=/base/${KSIM_BASE:?}; work=/base/.build.qcow2; log=/base/build.log
rm -f "$work" /base/.seed.iso "$out.new"; : > "$log"
qemu-img create -q -f qcow2 -F qcow2 -b /base/noble.img "$work" 20G
printf 'instance-id: ksim-build\nlocal-hostname: ksim-build\n' > /tmp/meta-data
cloud-localds /base/.seed.iso /build/provision.sh /tmp/meta-data
# the guest powers itself off when provision.sh is through; -no-reboot turns that into QEMU exiting
timeout "${KSIM_BUILD_TIMEOUT:-2400}" qemu-system-x86_64 "${KVM[@]}" -smp 4 -m 4096 -serial "file:$log" -no-reboot \
-drive file="$work",if=virtio,cache=unsafe -drive file=/base/.seed.iso,if=virtio,format=raw \
-netdev user,id=n0 -device virtio-net-pci,netdev=n0 \
-virtfs local,path=/build,mount_tag=build,security_model=none,readonly=on,id=build || true
if ! grep -q KSIM-PROVISION-OK "$log"; then
echo "provisioning did not finish - the end of the VM's console:" >&2; tail -n 40 "$log" >&2; rm -f "$work" /base/.seed.iso; exit 1
fi
qemu-img convert -O qcow2 "$work" "$out.new" && mv "$out.new" "$out"
rm -f "$work" /base/.seed.iso
;;
*) echo "usage: ksim-vm run|build" >&2; exit 2 ;;
esac