diff --git a/qemuimg2disk/Dockerfile b/qemuimg2disk/Dockerfile index a43ea98..58c1251 100644 --- a/qemuimg2disk/Dockerfile +++ b/qemuimg2disk/Dockerfile @@ -1,6 +1,6 @@ FROM alpine:3.21 AS builder ENV QEMU_NAME="qemu" -ARG QEMU_REV="6.1.0" +ARG QEMU_REV="11.0.1" ENV QEMU_SRC_BASENAME="${QEMU_NAME}-${QEMU_REV}" ENV QEMU_SRC_URL="https://download.qemu.org/${QEMU_SRC_BASENAME}.tar.xz" @@ -10,6 +10,8 @@ RUN apk add --update --upgrade \ libc-dev \ linux-headers \ make \ + meson \ + ninja \ perl \ pkgconf \ python3 py3-setuptools \ @@ -17,7 +19,7 @@ RUN apk add --update --upgrade \ zlib-dev zlib-static \ bash git parted patch xz \ curl-dev curl-static nettle-dev nettle-static brotli-dev brotli-static nghttp2-dev nghttp2-static openssl-libs-static \ - zstd-dev zstd-static libpsl-static libunistring-static libidn2-static + zstd-dev zstd-static libpsl-static libunistring-static libidn2-static bzip2-static WORKDIR /work RUN wget "${QEMU_SRC_URL}" -O- | tar xJ @@ -50,6 +52,11 @@ FROM scratch AS collect COPY --from=builder /bin/qemu-img /bin/qemu-img COPY --from=builder /bin/sh /bin/sh COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt +# qemu-img's static libcurl is built with --with-ca-bundle=/etc/ssl/cert.pem, +# so HTTPS sources fail without a CA bundle at that path. On Alpine this is a +# symlink, which `COPY --from` would preserve and break in the scratch image, +# so copy the resolved bundle directly. +COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/cert.pem COPY --from=builder /lib/ld-musl* /lib/ COPY --from=qemuimg2disk /src/qemuimg2disk/qemuimg2disk /usr/bin/qemuimg2disk COPY --from=qemuimg2disk /src/qemuimg2disk/entrypoint.sh /usr/bin/entrypoint.sh diff --git a/qemuimg2disk/entrypoint.sh b/qemuimg2disk/entrypoint.sh index 94814b0..082feda 100755 --- a/qemuimg2disk/entrypoint.sh +++ b/qemuimg2disk/entrypoint.sh @@ -3,5 +3,5 @@ set -o errexit set -o xtrace # We actually do want EXTRA_ARGS to be split up #shellcheck disable=SC2086 -qemu-img convert "${IMG_URL:?}" -O "${FORMAT:-host_device}" "${DEST_DISK:?}" ${EXTRA_ARGS} +qemu-img convert -p "${IMG_URL:?}" -O "${FORMAT:-host_device}" "${DEST_DISK:?}" ${EXTRA_ARGS} qemuimg2disk || true diff --git a/qemuimg2disk/test-local.sh b/qemuimg2disk/test-local.sh new file mode 100755 index 0000000..c87053f --- /dev/null +++ b/qemuimg2disk/test-local.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash +# Local repro for the qemuimg2disk action. +# +# The action runs `qemu-img convert -O host_device "$DEST_DISK"` and +# host_device only accepts real block/char devices. To test locally we +# back a temp raw file with a loop device and pass that through. +# +# Requires root (losetup + /dev/loopN access). +# +# Usage: +# sudo ./test-local.sh +# sudo IMG_URL=... ./test-local.sh +# sudo IMAGE=qemuimg2disk:local ./test-local.sh + +set -o errexit +set -o nounset +set -o pipefail + +if [[ "${EUID}" -ne 0 ]]; then + echo "must be run as root (need losetup + block device access)" >&2 + exit 1 +fi + +IMG_URL="${IMG_URL:-https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img}" +FORMAT="${FORMAT:-host_device}" +SIZE="${SIZE:-10G}" +IMAGE="${IMAGE:-quay.io/tinkerbell/actions/qemuimg2disk:latest}" + +workdir="$(mktemp -d -t qemuimg2disk-XXXXXX)" +disk="${workdir}/disk.raw" +loopdev="" + +cleanup() { + if [[ -n "${loopdev}" ]]; then + losetup -d "${loopdev}" 2>/dev/null || true + fi + rm -rf "${workdir}" +} +trap cleanup EXIT + +# Sparse raw file: claims SIZE bytes but uses ~0 until written to. +truncate -s "${SIZE}" "${disk}" + +# Attach it to a free loop device so qemu-img sees a real block device. +loopdev="$(losetup --find --show "${disk}")" + +echo "==> Temp file: ${disk} (${SIZE} sparse)" +echo "==> Loop dev: ${loopdev}" +echo "==> Source: ${IMG_URL}" +echo "==> Format: ${FORMAT}" +echo "==> Image: ${IMAGE}" +echo + +set -x +docker run --rm \ + -e IMG_URL="${IMG_URL}" \ + -e DEST_DISK="${loopdev}" \ + -e FORMAT="${FORMAT}" \ + --device "${loopdev}:${loopdev}" \ + "${IMAGE}" +set +x + +echo +echo "==> Done."