From 072766ea34b7f6b32a590f59814e30e99fe15bfa Mon Sep 17 00:00:00 2001 From: "re:fi.64" Date: Tue, 21 Jul 2026 20:43:19 -0500 Subject: [PATCH] nix/spindle: add almalinux10 image This adds an AlmaLinux microvm image, in order to be able to run tests on a RHEL-family distro. There are a couple of notable points of divergence from the Alpine image: - The microvm spec enables PCI, because RHEL/AlmaLinux only ships with CONFIG_VIRTIO_MMIO on arm64, and thus the virtio devices won't show up unless they go on PCI instead. - There isn't an easily attainable standalone initramfs suited for microvms; the only standalone one in general is for network booting and is absolutely massive. Thus, the kernel + initramfs are instead pulled from a microvm-focused UKI, which is much more minimalistic and *mostly* covers what we need (except for a few extra virtio drivers that get added a bit later on, pulled from the relevant RPMs). - Since the initramfs already uses systemd, and the rootfs ships it, it's used to boot the remainder of the image, for consistency with the expectations of a typical AL10 setup. - doas is installed w/ the sudo shim. Alpine doesn't really *need* this because apk is marked as setuid, but AL10's default rootfs still ships Python-powered dnf4, and you can't setuid scripts with a hashbang. (This is a bit more useful than just having the package manager be setuid, anyway.) Additionally, I went and added comments for some bits of the setup (in particular the kernel parameters) that weren't immediately clear to me when I was reading through the alpine image definition. For the tests, most of them were copied from Alpine, but the nix test is instead reworked to be generic (since nothing other than naming actually depends on the distro), to avoid too much duplication. The crash log tail size also needs to be bumped to fit in the larger surrounding logs. --- docs/DOCS.md | 4 +- flake.nix | 48 +++ localinfra/scripts/prepare-spindle-images.sh | 1 + nix/pkgs/spindle-almalinux-image.nix | 274 ++++++++++++++++++ spindle/engines/microvm/README.md | 12 +- .../engines/microvm/test-spindle-microvm.sh | 118 +++++++- spindle/engines/microvm/vm.go | 2 +- 7 files changed, 435 insertions(+), 24 deletions(-) create mode 100644 nix/pkgs/spindle-almalinux-image.nix diff --git a/docs/DOCS.md b/docs/DOCS.md index 82614faa..2977e303 100644 --- a/docs/DOCS.md +++ b/docs/DOCS.md @@ -1115,7 +1115,7 @@ There are two flavours of images: `registry` and `caches` fields below are all understood here, and the guest builds and activates that configuration before any of your steps run. -- **Non-NixOS images** (e.g. `alpine`): there's no NixOS to +- **Non-NixOS images** (e.g. `alpine`, `almalinux10`): there's no NixOS to configure, so the workflow-level config fields above have no effect. You still get a full machine to run steps in. @@ -1600,6 +1600,8 @@ tarball you can copy to another host): nix build .#spindle-nixos-image # an Alpine image nix build .#spindle-alpine-image +# an AlmaLinux 10 image +nix build .#spindle-almalinux10-image ``` #### Installing images diff --git a/flake.nix b/flake.nix index 673d8be4..40261a0e 100644 --- a/flake.nix +++ b/flake.nix @@ -337,6 +337,54 @@ spindle-alpine-image-tarball = linuxPkgs.runCommand "spindle-alpine-image-tarball.tar.gz" {} '' tar -S -C ${self.packages.${system}.spindle-alpine-image} -h -czf $out . ''; + + spindle-almalinux10-image = let + branch = "10"; + version = "${branch}.2"; + arch = "x86_64"; + goarch = "amd64"; + kver = "6.12.0-211.34.1.el10_2.${arch}"; + + # Every time a new point release comes out, and the previous one is deprecated, + # those repos get moved to the vault URL. However, for CI purposes, it + # would be annoying if the image completely stopped working from a new + # release being available, so we just fall back to the vault URL until + # the RPMs can be updated. + makeRpmUrls = rpm: [ + "https://repo.almalinux.org/almalinux/${version}/BaseOS/${arch}/os/Packages/${rpm}" + "https://vault.almalinux.org/almalinux/${version}/BaseOS/${arch}/os/Packages/${rpm}" + ]; + in + linuxPkgs.callPackage ./nix/pkgs/spindle-almalinux-image.nix { + inherit arch kver spindle-image-helpers; + rootfs = linuxPkgs.fetchurl { + url = "https://github.com/AlmaLinux/container-images/raw/cd5582c41aaf7f695b2473264b322f4080066796/default/${goarch}/almalinux-${branch}-default-${goarch}.tar.xz"; + hash = "sha256-60TIMFmaCoPv//XLAm6Cm/wkpmtbwH996zgM7CB/ypQ="; + }; + kernel-modules-core = linuxPkgs.fetchurl { + urls = makeRpmUrls "kernel-modules-core-${kver}.rpm"; + hash = "sha256-KYR/dnm/oCglQ4uvFTZlW6WBR1HBUNh/yS64PxyIQvk="; + }; + kernel-modules = linuxPkgs.fetchurl { + urls = makeRpmUrls "kernel-modules-${kver}.rpm"; + hash = "sha256-n2SPQuM+dmTQn4Ustkl9GyF8xH3godU0fjJO8oz4JDY="; + }; + uki = linuxPkgs.fetchurl { + urls = makeRpmUrls "kernel-uki-virt-${kver}.rpm"; + hash = "sha256-ds2+uSPpuHHoVyYaUrh8DSwWgVWYMijgGoneAE+JP9I="; + }; + # doas-sudo-shim 0.1.x in the repos requires util-linux, which isn't in the + # container and is annoying to get inside, so just get it ourselves. + doas-sudo-shim-src = linuxPkgs.fetchFromGitHub { + owner = "jirutka"; + repo = "doas-sudo-shim"; + rev = "v0.2.0"; + sha256 = "sha256-USSakVUzCbUY1DJLmDCiwdq/xjOwwnm3VtXBBeXeV1A="; + }; + }; + spindle-almalinux10-image-tarball = linuxPkgs.runCommand "spindle-almalinux10-image-tarball.tar.gz" {} '' + tar -S -C ${self.packages.${system}.spindle-almalinux10-image} -h -czf $out . + ''; }); defaultPackage = forAllSystems (system: self.packages.${system}.appview); devShells = forAllSystems (system: let diff --git a/localinfra/scripts/prepare-spindle-images.sh b/localinfra/scripts/prepare-spindle-images.sh index 93f31cf4..cb283db9 100755 --- a/localinfra/scripts/prepare-spindle-images.sh +++ b/localinfra/scripts/prepare-spindle-images.sh @@ -28,5 +28,6 @@ extract_image() { extract_image spindle-nixos-image-tarball nixos-x86_64 nixos extract_image spindle-alpine-image-tarball alpine-x86_64 alpine +extract_image spindle-almalinux10-image-tarball almalinux10-x86_64 almalinux10 almalinux echo "prepared spindle microVM images in $image_root" diff --git a/nix/pkgs/spindle-almalinux-image.nix b/nix/pkgs/spindle-almalinux-image.nix new file mode 100644 index 00000000..db810779 --- /dev/null +++ b/nix/pkgs/spindle-almalinux-image.nix @@ -0,0 +1,274 @@ +{ + doas-sudo-shim-src, + pkgsStatic, + runCommand, + writeText, + python3, + squashfsTools, + spindle-image-helpers, + binutils, + kmod, + libarchive, + zstd, + kver, + rootfs, + kernel-modules-core, + kernel-modules, + uki, + arch ? "x86_64", +}: let + # packages pulled from pkgsStatic will have their CA path set to the nix + # default (/etc/ssl/certs/ca-certificates.crt), which doesn't match the + # RHEL/AL path, so it'll need to be overridden manually in the various + # packages + caPath = "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem"; + + busybox = pkgsStatic.busybox; + nix = pkgsStatic.nixStatic; + jq = pkgsStatic.jq; + curlMinimal = pkgsStatic.curlMinimal.overrideAttrs (old: { + configureFlags = (old.configureFlags or []) ++ ["--with-ca-bundle=${caPath}"]; + }); + git = pkgsStatic.callPackage ./spindle-static-git.nix { + inherit curlMinimal; + }; + doas = pkgsStatic.doas.override { + withPAM = false; + }; + guestTools = [nix git jq doas]; + + pseudoDefinitionsGenerator = writeText "pseudo-definitions-generator.py" '' + # prints out mksquashfs pseudo-definitions to restore the setuid/caps state + # from the input tar file + + import base64 + import os.path + import stat + import sys + import tarfile + + with tarfile.open(fileobj=sys.stdin.buffer, mode='r|*') as tar: + for m in tar: + name = os.path.normpath(os.path.join('/', m.name)) + + if m.mode & (stat.S_ISUID | stat.S_ISGID) != 0: + print(f'{name} m 0{m.mode & 0o7777:o} {m.uid} {m.gid}') + + for k, v in m.pax_headers.items(): + if k.startswith('LIBARCHIVE.xattr'): + raw = base64.b64decode(v) + elif k.startswith('SCHILY.xattr'): + # go from the encoded value back to the raw bytes + raw = v.encode('utf-8', 'surrogateescape') + else: + continue + + attr = k.split('.xattr.', 1)[1] + print(f'{name} x {attr}=0x{raw.hex()}') + ''; + + modulesConf = writeText "spindle-modules.conf" '' + vmw_vsock_virtio_transport + # shuttle's cache enqueue listener binds a guest-local (CID 1) vsock + vsock_loopback + ext4 + ''; + + initService = writeText "spindle-init.service" '' + [Unit] + Description=Spindle system initialization + + [Service] + Type=oneshot + RemainAfterExit=yes + # on failure, /workspace is in an unclear state, so don't bother retrying + Restart=no + ExecStart=spindle-system-init + ''; + + shuttleService = writeText "shuttle.service" '' + [Unit] + Description=Shuttle + + [Service] + Type=simple + ExecStart=shuttle + Environment=NIX_REMOTE=daemon + ''; + + nixSocket = writeText "nix-daemon.socket" '' + [Unit] + Description=Nix daemon socket + + [Socket] + ListenStream=/nix/var/nix/daemon-socket/socket + ''; + + nixService = writeText "nix-daemon.service" '' + [Unit] + Description=Nix daemon + Requires=spindle-init.service + After=spindle-init.service + + [Service] + ExecStart=nix-daemon --daemon + KillMode=process + Environment=TMPDIR=/workspace/.nix/build + ''; + + doasConf = writeText "doas.conf" '' + permit nopass spindle-workflow as root + ''; + + imageSpecJSON = writeText "spec.json" ( + builtins.toJSON { + inherit arch; + bootArgs = builtins.toString [ + # use the serial console only during early boot, then use the virtio console + # (if debugging virtio_console issues, the latter to 'console=ttyS0' so + # logs don't just get eaten) + "earlyprintk=ttyS0" + "console=hvc0" + + # reboot via triple fault: + # https://www.qemu.org/docs/master/system/i386/microvm.html#triggering-a-guest-initiated-shut-down + # > The recommended way to trigger a guest-initiated shut down is by + # > generating a triple-fault, > which will cause the VM to initiate a + # > reboot". + "reboot=triple" + # reboot immediately on panic + "panic=-1" + + "root=/dev/vda" + "rootfstype=squashfs" + # volatile rootfs + "systemd.volatile=overlay" + # selinux is not particularly useful for microvm CI + "selinux=0" + + # enable this to forward journal logs to the console, for debugging + # "systemd.journald.forward_to_console=1" + ]; + kernel = "kernel"; + initrd = "initrd"; + runnerType = "qemu"; + runnerConfig = { + cpu = "host,+x2apic,-sgx"; + machine = "microvm,accel=kvm:tcg,acpi=on,mem-merge=on,pcie=on,pic=off,pit=off,rtc=on,usb=off"; + console = "hvc0"; + extraArgs = []; + # RHEL/AlmaLinux kernels are built with CONFIG_VIRTIO_MMIO, so use PCI + # instead (this is why pcie=on in the `machine` line above, instead of + # `pcie=off` like other VM images) + virtioTransport = "pci"; + }; + memoryMiB = 4096; + storeDisk = "store-disk"; + storeDiskType = "squashfs"; + vcpus = 2; + shell = "/usr/bin/bash"; + networkInterfaces = [ + { + type = "slirp4netns"; + id = "net0"; + mac = "02:00:00:00:10:01"; + } + ]; + volumes = [ + { + fsType = "ext4"; + image = "workspace.img"; + imageType = "raw"; + mountPoint = "/workspace"; + readOnly = false; + sizeMiB = 1024 * 16; # 16 GB + } + ]; + } + ); +in + runCommand "spindle-almalinux-image-${arch}" { + nativeBuildInputs = [squashfsTools binutils kmod libarchive python3 zstd]; + } '' + mkdir -p rootfs + bsdtar -xpf ${rootfs} -C rootfs --no-xattrs + # we'll need to write to here later + chmod -R u+w rootfs/{usr/{bin,sbin},etc/shadow} + + # set up the pseudo definitions to restore setuid/caps at the end + python3 ${pseudoDefinitionsGenerator} < ${rootfs} > pseudo-file.txt + # doas will need setuid, too + echo '/usr/local/bin/doas m 04755 0 0' >> pseudo-file.txt + + # extract the uki to get the kernel + initramfs + # (the UKI is used here because there is no better-suitable initramfs + # available standalone—the network booting version is utterly massive and + # contains many modules not needed for the microvm) + bsdtar -xf ${uki} --strip-components=4 './lib/modules/*/vmlinuz-virt.efi' + objcopy -O binary -j.linux vmlinuz-virt.efi vmlinuz + objcopy -O binary -j.initrd vmlinuz-virt.efi initrd + + # add some additional necessary modules to the initrd + mkdir initrd-tree + bsdtar -C initrd-tree -xf initrd + # modules and dependencies: + # - squashfs + # - virtio_console + # - virtio_net -> net_failover -> failover + # - vmw_vsock_virtio_transport -> [a bunch of other stuff in vmw_vsock] + bsdtar -C initrd-tree/usr -xf ${kernel-modules-core} \ + "./lib/modules/${kver}/kernel/drivers/char/virtio_console.ko.xz" \ + "./lib/modules/${kver}/kernel/drivers/net/net_failover.ko.xz" \ + "./lib/modules/${kver}/kernel/drivers/net/virtio_net.ko.xz" \ + "./lib/modules/${kver}/kernel/net/core/failover.ko.xz" \ + "./lib/modules/${kver}/kernel/net/vmw_vsock/*" + bsdtar -C initrd-tree/usr -xf ${kernel-modules} \ + "./lib/modules/${kver}/kernel/fs/squashfs/squashfs.ko.xz" + find initrd-tree -name '*.ko.xz' -exec xz -d '{}' + + depmod -b initrd-tree ${kver} # regenerate modules.dep + # place the modules-load.d configuration in the initramfs, because + # systemd-modules-load does not exist in the default rootfs + install -D -m 0644 ${modulesConf} initrd-tree/etc/modules-load.d/spindle-modules.conf + (cd initrd-tree && find . | bsdtar --format=newc --uid=0 --gid=0 -cnf - -T -) \ + | zstd -12 > initrd + + cp -a initrd-tree/usr/lib/modules rootfs/usr/lib + + ${spindle-image-helpers.setupRootfs} rootfs + + install -D -m 0644 ${initService} rootfs/usr/lib/systemd/system/spindle-init.service + install -D -m 0644 ${shuttleService} rootfs/usr/lib/systemd/system/shuttle.service + + # nix daemon + install -D -m 0644 ${nixSocket} rootfs/usr/lib/systemd/system/nix-daemon.socket + install -D -m 0644 ${nixService} rootfs/usr/lib/systemd/system/nix-daemon.service + echo 'ssl-cert-file = ${caPath}' >> rootfs/etc/nix/nix.conf # add the SSL configuration + + # doas + install -D -m 0644 ${doasConf} rootfs/etc/doas.conf + install -Dm 755 ${doas-sudo-shim-src}/sudo -t rootfs/usr/local/bin + + # enable some services by default + ln -s \ + ../nix-daemon.socket \ + ../spindle-init.service \ + ../shuttle.service \ + rootfs/usr/lib/systemd/system/multi-user.target.wants/ + + # install dependencies + ${spindle-image-helpers.installGuestTools} rootfs ${toString guestTools} + + # busybox is needed for the ip tools to work (iproute2 is a MUCH larger + # dependency), but we don't include it in the above loop because we don't + # want any of the symlinks (since AL already ships coreutils) + cp ${busybox}/bin/busybox rootfs/usr/local/bin + + mkdir -p "$out" + mksquashfs rootfs "$out/store-disk" \ + -comp zstd -Xcompression-level 19 \ + -noappend -all-root -quiet \ + -pf pseudo-file.txt + cp vmlinuz "$out/kernel" + cp initrd "$out/initrd" + cp ${imageSpecJSON} "$out/spec.json" + '' diff --git a/spindle/engines/microvm/README.md b/spindle/engines/microvm/README.md index fb9eb925..51396200 100644 --- a/spindle/engines/microvm/README.md +++ b/spindle/engines/microvm/README.md @@ -17,13 +17,13 @@ Currently two kinds of images are supported: `virtualisation`, `registry`, `caches` in the workflow file itself. The guest agent will build (or if it's cached, spindle will send the store path for realization) and activate it before any workflow steps are ran. -- Non-NixOS: this is mainly just Alpine for now, but can be anything else. +- Non-NixOS: this is just Alpine & AlmaLinux for now, but can be anything else. Workflow-level configuration like NixOS aren't supported while using these. If Nix exists inside the image (like in our Alpine image) it will still be able to make use of the spindle cache. (For testing, you can run `bash spindle/engines/microvm/test-spindle-microvm.sh` -from repo root. These test the Alpine & NixOS, and features like if Docker +from repo root. These test Alpine, AlmaLinux, and NixOS, and features like if Docker works, public internet is reachable, and so on.) ## Image builds @@ -32,10 +32,10 @@ Image builds right now are done via Nix: - For NixOS, we use [microvm.nix](https://github.com/microvm-nix/microvm.nix), and layer our own configs on-top, see [here](../../../nix/microvm). -- For Alpine we have a small-ish Nix definition that includes fetching the - kernel, initrd, kernel modules; setting up the init script that configures the - VM proper; copying dependencies (like `nix` or `git`) into a rootfs and - creating a squashfs from it. +- For Alpine and AlmaLinux we have a small-ish Nix definition that includes + fetching the kernel, initrd, kernel modules; setting up the init system that + configures the VM proper; copying dependencies (like `nix` or `git`) into a + rootfs and creating a squashfs from it. This does not mean it *has* to be done via Nix, as long as your images are what spindle expects, they should work. That is: diff --git a/spindle/engines/microvm/test-spindle-microvm.sh b/spindle/engines/microvm/test-spindle-microvm.sh index 68e2ee73..98226f32 100755 --- a/spindle/engines/microvm/test-spindle-microvm.sh +++ b/spindle/engines/microvm/test-spindle-microvm.sh @@ -215,6 +215,12 @@ mkdir -p "$TEMP_DIR/alpine-image" tar -C "$TEMP_DIR/alpine-image" -xzf "$ALPINE_TARBALL_PATH" ALPINE_IMAGE_SPEC_JSON="$TEMP_DIR/alpine-image/spec.json" +log "build almalinux10 microvm image tarball" +AL10_TARBALL_PATH=$(nix build .#spindle-almalinux10-image-tarball --no-link --print-out-paths) +mkdir -p "$TEMP_DIR/almalinux10-image" +tar -C "$TEMP_DIR/almalinux10-image" -xzf "$AL10_TARBALL_PATH" +AL10_IMAGE_SPEC_JSON="$TEMP_DIR/almalinux10-image/spec.json" + kill_temp_dir_procs() { if [ -f "$TEMP_DIR/ncps.pid" ]; then kill "$(cat "$TEMP_DIR/ncps.pid")" 2>/dev/null || true @@ -890,9 +896,12 @@ cache_has_path() { return 1 } -test_alpine_nix() { +test_generic_distro_nix() { + local name="$1" + local spec="$2" + local test_store_path - test_store_path=$(nix-build -E 'with import {}; writeText "alpine-nix-test" "hello from cache to alpine"' --no-out-link) + test_store_path=$(nix-build -E 'with import {}; writeText "'$name'-nix-test" "hello from cache to '$name'"' --no-out-link) nix copy --to "$CACHE_UPLOAD_URL?secret-key=$CACHE_SECRET_KEY_PATH" "$test_store_path" # exercise the full local-cache path: daemon connectivity, substitution, @@ -900,10 +909,11 @@ test_alpine_nix() { # new flakes/nix-command (nix build) frontends. the two build derivations # use distinct names so we can confirm each got uploaded back to the cache. local out - out=$(run_vm --spec "$ALPINE_IMAGE_SPEC_JSON" --name "alpine-nix" --timeout "180s" --upload -- /bin/sh -lc ' + out=$(run_vm --spec "$spec" --name "$name-nix" --timeout "180s" --upload -- /bin/sh -lc ' set -eu export HOME=/workspace store_path=$1 +name=$2 echo "nix_version=$(nix --version | head -n1)" { nix store info >/dev/null 2>&1 || nix store ping >/dev/null 2>&1; } && echo "daemon=ok" @@ -922,7 +932,7 @@ export DEP="$store_path" cat > /workspace/new.nix < /workspace/old.nix < \$out" ]; + args = [ "-c" "echo built-on-$name > \$out" ]; } NIXEOF old_path=$(nix-build /workspace/old.nix --no-out-link) echo "old_path=$old_path" -' sh "$test_store_path") || return 1 +' sh "$test_store_path" "$name") || return 1 check_needles "$out" \ - "daemon=ok" "substituted=hello from cache to alpine" "path_info=ok" \ + "daemon=ok" "substituted=hello from cache to $name" "path_info=ok" \ "requisites=[1-9][0-9]*" "new_content=via-nix-build-with-dep" || return 1 local clean new_path old_path clean=$(echo "$out" | strip_ansi) - new_path=$(echo "$clean" | grep -o 'new_path=/nix/store/[a-z0-9]*-alpine-nix-build-new' | cut -d= -f2) - old_path=$(echo "$clean" | grep -o 'old_path=/nix/store/[a-z0-9]*-alpine-nix-build-old' | cut -d= -f2) + new_path=$(echo "$clean" | grep -o "new_path=/nix/store/[a-z0-9]*-$name-nix-build-new" | cut -d= -f2) + old_path=$(echo "$clean" | grep -o "old_path=/nix/store/[a-z0-9]*-$name-nix-build-old" | cut -d= -f2) if [ -z "$new_path" ] || [ -z "$old_path" ]; then - echo "error: could not extract both built store paths from alpine guest output" >&2 + echo "error: could not extract both built store paths from guest output" >&2 return 1 fi if ! cache_has_path "$new_path"; then @@ -968,12 +978,81 @@ echo "old_path=$old_path" echo "error: nix-build (classic CLI) output was not uploaded to the cache" >&2 return 1 fi - echo "success: alpine guest substituted, queried the store db, built via both CLIs, and uploaded both outputs" + echo "success: guest substituted, queried the store db, built via both CLIs, and uploaded both outputs" +} + +test_alpine_nix() { + test_generic_distro_nix alpine $ALPINE_IMAGE_SPEC_JSON +} + +test_almalinux10() { + local hello_path + hello_path=$(nix-build -E 'with import {}; hello' --no-out-link) + + local out + out=$(run_vm --spec "$AL10_IMAGE_SPEC_JSON" --name "almalinux10" --timeout "180s" --no-cache -- /bin/sh -lc ' +set -eu +export HOME=/workspace +hello_path=$1 +echo "release=$(cat /etc/almalinux-release)" +echo "user=$(id -un)" +git version +bash -c "echo bash=\$BASH_VERSION" +touch /workspace/write-test +echo "workspace writable" +git ls-remote https://tangled.org/@tangled.org/core HEAD >/dev/null +echo "git over https ok" +sudo dnf install -y make +echo "dnf ok" +# substitute a real package from cache.nixos.org over HTTPS and run it +nix-store --realise "$hello_path" >/dev/null +echo "ran=$("$hello_path/bin/hello")" +' sh "$hello_path") || return 1 + + check_needles "$out" \ + "release=" "user=spindle-workflow" "git version" "bash=" \ + "workspace writable" "git over https ok" "dnf ok" "ran=Hello, world!" || return 1 + echo "success: almalinux10 guest booted, ran as workflow user, wrote workspace, cloned + installed over the network, and substituted+ran a package from cache.nixos.org over HTTPS" +} + +test_almalinux10_podman() { + # install podman via dnf and run a real container as the workflow user. + # rootless podman lives entirely in the writable workspace (storage + runroot + # under XDG dirs there), uses podman's default storage driver, and pulls over + # the guest network like the other alpine tests. + local out + out=$(run_vm --spec "$AL10_IMAGE_SPEC_JSON" --name "almalinux10-podman" --timeout "300s" --no-cache -- /bin/sh -lc ' +set -eu +# no env setup here on purpose: shuttle seeds USER/LOGNAME/HOME/SHELL from the +# workflow users passwd entry and provisions XDG_RUNTIME_DIR, so rootless podman +# works out of the box. asserting those below doubles as a check on that. + +sudo dnf install -y podman +echo "user=$(id -un) USER=$USER HOME=$HOME XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR" +echo "newuidmap=$(command -v newuidmap)" +echo "podman_version=$(podman --version)" + +podman info >/dev/null +echo "storage_driver=$(podman info --format "{{.Store.GraphDriverName}}")" + +podman run --rm --network=host docker.io/library/alpine cat /etc/alpine-release | sed "s/^/container_release=/" +podman run --rm --network=host docker.io/library/alpine echo container-ran-ok +' sh) || return 1 + + check_needles "$out" \ + "user=spindle-workflow USER=spindle-workflow HOME=/workspace XDG_RUNTIME_DIR=/run/user/970" \ + "newuidmap=/" "podman_version=" \ + "storage_driver=" "container_release=[0-9]+\." "container-ran-ok" || return 1 + echo "success: almalinux10 guest installed podman via apk and pulled + ran a rootless container" +} + +test_almalinux10_nix() { + test_generic_distro_nix almalinux10 $AL10_IMAGE_SPEC_JSON } test_oom_detection() { local label spec - for label in "alpine" "nixos"; do + for label in "almalinux10" "alpine" "nixos"; do echo "testing oom on $label..." local work_dir="$TEMP_DIR/work-oom-test-$label" mkdir -p "$work_dir" @@ -982,14 +1061,18 @@ test_oom_detection() { if [ "$label" = "alpine" ]; then spec="$ALPINE_IMAGE_SPEC_JSON" cmd_args=(awk 'BEGIN { while(1) a[i++]=1 }') + elif [ "$label" = "almalinux10" ]; then + spec="$AL10_IMAGE_SPEC_JSON" + # intermediate generator required to avoid a boring MemoryError + cmd_args=(python3 -c 'list(i for i in range(999999999))') else spec="$IMAGE_SPEC_JSON" cmd_args=(/run/current-system/sw/bin/jq -n '[repeat(1)]') fi - local mem_mib=128 - if [ "$label" = "nixos" ]; then - mem_mib=512 + local mem_mib=512 + if [ "$label" = "alpine" ]; then + mem_mib=128 fi local args=( @@ -1013,6 +1096,9 @@ test_oom_detection() { } TESTS=( + test_almalinux10 + test_almalinux10_nix + test_almalinux10_podman test_alpine test_alpine_nix test_alpine_podman diff --git a/spindle/engines/microvm/vm.go b/spindle/engines/microvm/vm.go index 0ae4db49..35c85fa3 100644 --- a/spindle/engines/microvm/vm.go +++ b/spindle/engines/microvm/vm.go @@ -25,7 +25,7 @@ import ( const ( minGuestCID = 3 - vmCrashLogTailBytes = 4096 + vmCrashLogTailBytes = 8192 ) func AllocateCID() (uint32, error) { -- 2.51.2