From f183028989024426a48f1ce83023fff94adb43a3 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Mon, 14 Jul 2025 15:22:11 +0200 Subject: [PATCH 01/10] config: use /usr/bin/env which to find qemu Signed-off-by: Joel Granados --- examples/vm/nvme-aarch64.conf | 2 +- examples/vm/nvme.conf | 2 +- examples/vm/x86_64-q35-base.conf | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/examples/vm/nvme-aarch64.conf b/examples/vm/nvme-aarch64.conf index 1a33d93..89df9ee 100644 --- a/examples/vm/nvme-aarch64.conf +++ b/examples/vm/nvme-aarch64.conf @@ -1,6 +1,6 @@ #!/usr/bin/env bash -QEMU_SYSTEM_AARCH64=$(/usr/bin/which qemu-system-aarch64) +QEMU_SYSTEM_AARCH64=$(/usr/bin/env which qemu-system-aarch64) GUEST_BOOT_BASE="img/debian-13-genericcloud-arm64.qcow2" source "aarch64-virt-base.conf" diff --git a/examples/vm/nvme.conf b/examples/vm/nvme.conf index c51db08..9e2a9ae 100644 --- a/examples/vm/nvme.conf +++ b/examples/vm/nvme.conf @@ -1,6 +1,6 @@ #!/usr/bin/env bash -QEMU_SYSTEM_X86_64=$(/usr/bin/which qemu-system-x86_64) +QEMU_SYSTEM_X86_64=$(/usr/bin/env which qemu-system-x86_64) source "x86_64-q35-base.conf" #source "x86_64-q35-noimgnix-base.conf" diff --git a/examples/vm/x86_64-q35-base.conf b/examples/vm/x86_64-q35-base.conf index 16d7c43..c227aa1 100644 --- a/examples/vm/x86_64-q35-base.conf +++ b/examples/vm/x86_64-q35-base.conf @@ -18,8 +18,7 @@ if [[ -f "common.conf" ]]; then source "common.conf" fi -QEMU_SYSTEM_BINARY=${QEMU_SYSTEM_X86_64} - +: "${QEMU_SYSTEM_BINARY:="$(/usr/bin/env which qemu-system-x86_64)"}" : "${GUEST_DISPLAY:="0"}" : "${GUEST_VIOMMU:="1"}" : "${GUEST_VIOMMU_ARGS:="intel-iommu,intremap=on"}" From a453996b8c0eaab274087a52333e3808323c3abc Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Mon, 14 Jul 2025 14:41:14 +0200 Subject: [PATCH 02/10] rc: Add VMROOT only when qcow base image path is relative Signed-off-by: Joel Granados --- lib/qemu/rc | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/qemu/rc b/lib/qemu/rc index b142028..d25dd6f 100644 --- a/lib/qemu/rc +++ b/lib/qemu/rc @@ -87,7 +87,7 @@ qemu_drive_add() { while true; do case "$1" in '--file' ) - local file="/$2"; shift 2 + local file="$2"; shift 2 ;; '--format' ) @@ -139,11 +139,9 @@ qemu_drive_add() { local id="$1" if [[ ! -v file ]]; then - local file="/state/${VMNAME}/${id}.img" - fi - - if [[ ! -v DOCKER_IMAGE ]]; then - file="${VMROOT}${file}" + local file="${VMROOT}/state/${VMNAME}/${id}.img" + elif [[ "${file}" != /* ]]; then + file="${VMROOT}/${file}" fi if [[ -v do_create ]]; then From 16d26e84efbc5e0e530051b7febe86df5974a737 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Mon, 14 Jul 2025 14:44:48 +0200 Subject: [PATCH 03/10] cloudinit: add config file for image initialization Signed-off-by: Joel Granados --- examples/vm/cloudinit.conf | 155 +++++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 examples/vm/cloudinit.conf diff --git a/examples/vm/cloudinit.conf b/examples/vm/cloudinit.conf new file mode 100644 index 0000000..fa9dbd5 --- /dev/null +++ b/examples/vm/cloudinit.conf @@ -0,0 +1,155 @@ +#!/usr/bin/env bash + +CLOUD_INIT_STAGE_DIR="$(mktemp -d)" +trap 'rm -rfv ${CLOUD_INIT_STAGE_DIR}' EXIT + +#CLOUD_INIT_SSHKEY="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFAKEKEYPLACEHOLDER ubuntu@placeholder" +CLOUD_INIT_STAGE_FILE="${CLOUD_INIT_STAGE_DIR}/seed.img" +CLOUD_INIT_OUTPUT_IMG="img/base.qcow2" +GUEST_BOOT_BASE="${CLOUD_INIT_STAGE_DIR}/base.qcow2" +source "x86_64-q35-base.conf" + +cloudinit_wget_ubuntu() { + local cloud_img_path="$1" + local uimg="https://cloud-images.ubuntu.com/releases/plucky/release" + uimg="${uimg}/ubuntu-25.04-server-cloudimg-amd64.img" + wget -O "${cloud_img_path}" "${uimg}" +} + +cloudinit_resize_img() { + local cloud_img_path="$1" + qemu-img resize "${cloud_img_path}" 8G +} + +cloudinit_get_sshkey() { + if [[ -v CLOUD_INIT_SSHKEY ]]; then + echo "${CLOUD_INIT_SSHKEY}" + return 0 + fi + + SSH_KEY_FILE=$(find ~/.ssh -maxdepth 1 -type f -name "*.pub" | head -n 1) + + if [[ -z "$SSH_KEY_FILE" ]]; then + _fatal 1 "No public key found in ~/.ssh. Try setting CLOUD_INIT_SSHKEY." + fi + + # Read the key content + CLOUD_INIT_SSHKEY=$(cat "$SSH_KEY_FILE") + echo "${CLOUD_INIT_SSHKEY}" + return 0 +} + +cloudinit_make_userdatafile() { + local usr_data_file="$1" + local ssh_key="" + ssh_key="$(cloudinit_get_sshkey)" + + cat > "${usr_data_file}" << EOF +#cloud-config +disable_root: false +ssh_pwauth: false + +users: + - name: vmuser + sudo: ['ALL=(ALL) NOPASSWD:ALL'] + groups: users, admin + home: /home/vmuser + shell: /bin/bash + lock_passwd: false + plain_text_passwd: 'vmuser' + ssh_authorized_keys: + - ${ssh_key} + + - name: root + ssh_authorized_keys: + - ${ssh_key} + +write_files: +- path: /etc/systemd/system/mount-shared-kernel-dir.service + content: | + # Cribbed and slightly modified from a systemd unit-file created by Omar + # Sandoval: + # https://github.com/osandov/osandov-linux/blob/master/scripts/vm-modules-mounter.service + + [Unit] + Description=Mount shared kernel build dir + DefaultDependencies=no + After=systemd-remount-fs.service + Before=local-fs-pre.target systemd-modules-load.service systemd-udevd.service kmod-static-nodes.service umount.target + Conflicts=umount.target + RefuseManualStop=true + ConditionPathExists=!/lib/modules/%v/kernel + + [Install] + WantedBy=local-fs-pre.target + + [Service] + Type=oneshot + RemainAfterExit=yes + ExecStart=mount -t tmpfs -o mode=755,strictatime,x-mount.mkdir,x-initrd.mount tmpfs /lib/modules/%v + ExecStart=mount -t 9p -o trans=virtio,ro,x-mount.mkdir,x-initrd.mount kernel_dir /lib/modules/%v/build + ExecStart=ln -s build/modules.order /lib/modules/%v/modules.order + ExecStart=ln -s build/modules.builtin /lib/modules/%v/modules.builtin + ExecStart=ln -s build /lib/modules/%v/kernel + ExecStart=-depmod %v + ExecStopPost=sh -c 'if mountpoint -q /lib/modules/%v/build; then umount -l /lib/modules/%v/build; fi' + ExecStopPost=sh -c 'if mountpoint -q /lib/modules/%v; then umount -l /lib/modules/%v; fi' + ExecStopPost=find /lib/modules -mindepth 1 -maxdepth 1 -type d -empty -delete + ExecReload=-depmod %v + +runcmd: + - [ systemctl, daemon-reload ] + - [ systemctl, enable, mount-shared-kernel-dir.service ] + +power_state: + mode: poweroff + condition: true +EOF +} + +cloudinit_make_metadatafile() { + local meta_data_file="$1" + + cat > "${meta_data_file}" << EOF +#cloud-config +#instance-id: vmctl-1 +#local-hostname: vmctl +EOF +} + +cloudinit_make_seed_img() { + local seed_img_path="$1" + local usr_data_file="${CLOUD_INIT_STAGE_DIR}/user-data" + local meta_data_file="${CLOUD_INIT_STAGE_DIR}/meta-data" + + cloudinit_make_userdatafile "${usr_data_file}" + cloudinit_make_metadatafile "${meta_data_file}" + + mkisofs -output "${seed_img_path}" \ + -volid cidata -joliet -rock "${usr_data_file}" "${meta_data_file}" +} + +_pre() { + _require_program mkisofs + _require_program wget + local cloud_img="${GUEST_BOOT}" + + if [[ -f "${CLOUD_INIT_OUTPUT_IMG}" ]]; then + _fatal 1 "${CLOUD_INIT_OUTPUT_IMG} exist; rename to avoid overwriting" + fi + + cloudinit_wget_ubuntu "${cloud_img}" + cloudinit_resize_img "${cloud_img}" + cloudinit_make_seed_img "${CLOUD_INIT_STAGE_FILE}" +} + +_setup_cloudinit() { + _setup_x86_64_q35_base + + QEMU_PARAMS+=("-drive" "id=cloud-init-seed,file=${CLOUD_INIT_STAGE_FILE},format=raw,media=cdrom") +} + +_post() { + mkdir -p "img" + mv "${GUEST_BOOT}" "${CLOUD_INIT_OUTPUT_IMG}" +} From 62fef721dd68306c9725a230bf710fb767fdebd2 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Mon, 14 Jul 2025 22:59:44 +0200 Subject: [PATCH 04/10] cloudinit: Modify README to adjust for new cloudinit.conf Signed-off-by: Joel Granados --- README.md | 55 ++++++++++++++++++------------------------------------- 1 file changed, 18 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index fe391d2..ef0bab9 100644 --- a/README.md +++ b/README.md @@ -163,50 +163,31 @@ This is useful when selftests or modules are needed within the VM. ## Prep boot img -The base configruation `*-base.conf` will look for a base image in -`img/base.qcow2`. You can use [archbase][archbase] to build a lean Arch Linux -base image or grab a QCOW2-based [Ubuntu cloud image][ubuntu-cloud-image] if -that's your vice. +There needs to be a base image in `img/base.qcow2` because the default configs +`*-base.conf` expect it. Use the following command to create a QCOW2-based +[Ubuntu cloud image][ubuntu-cloud-image]: -In the case of a standard "cloud image", you probably want to resize it since -it is usually shrunk to be as small as possible by default. + $ vmctl -c cloudinit.conf run - $ qemu-img resize img/base.qcow2 8G +**Note**: This command will do the following: + 1. Create an ubuntu amd64 plucky image + 2. Resize it to 8G + 3. Add first ~/.ssh/*.pub file for ssh connections + 4. Configure default login:"vmuser" and passwd:"vmuser" + 5. It will do a qemu seed run and then powerdown to set everything up -**Note** The example `nvme.conf` will define `GUEST_BOOT="img/nvme.qcow2"`. -You do not need to provide that image - if it is not there `$GUEST_BOOT` -will be a differential image backed by `img/base.qcow2`. So, if you ever -need to reset to the "base" state, just remove the `img/nvme.qcow2` image. +After running the `cloudinit.conf` configuration, you should see an image in +`img/base.qcow2` which you can run by doing: -[archbase]: https://github.com/OpenMPDK/archbase -[ubuntu-cloud-image]: https://cloud-images.ubuntu.com - -### cloud-init - -If your chosen base image is meant to be configured through [cloud-init][cloud-init], -you can use the included cloud-config helper script to generate a basic -cloud-init seed image: - - $ ./contrib/generate-cloud-config-seed.sh ~/.ssh/id_rsa.pub - -If the image is running freebsd, use the script with `-freebsd` suffix: + $ vmctl -c nvme.conf run -b + $ vmctl -c nvme.conf ssh --wait - $ ./contrib/generate-cloud-config-seed-freebsd.sh ~/.ssh/id_rsa.pub +**Note**: Customize cloudinit.conf if the defaults don't work for you -This will generate a simple cloud-init seed image that will set up the image -with a default `vmuser` account that can be logged into using the given public -key. Place the output image (`seed.img`) in `img/` and pass the `--cloud-init` -(short: `'-c'`) option to `vmctl run` to initialize the image on first boot: - - $ vmctl -c CONFIG run -c - -cloud-init will automatically power off the virtual machine when it has been -configured. - -**Note**: For the cloud-config helper script to work `cloud-utils` is required. - -[cloud-init]: https://cloudinit.readthedocs.io/en/latest/ +**Note** Use [archbase][archbase] to build a lean Arch Linux base image +[archbase]: https://github.com/OpenMPDK/archbase +[ubuntu-cloud-image]: https://cloud-images.ubuntu.com ## License From 4439058f1e8030be65d3861665462dbfa4e64a34 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Mon, 14 Jul 2025 23:03:43 +0200 Subject: [PATCH 05/10] run: remove cloud-init Signed-off-by: Joel Granados --- cmd/run | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/cmd/run b/cmd/run index 2951f20..a4d3fcb 100644 --- a/cmd/run +++ b/cmd/run @@ -24,7 +24,6 @@ Options: -k, --kernel-dir DIR directory containing the kernel source. The directory will be made available to the vm as a 9p virtfs with mount tag 'kernel_dir' - -c, --cloud-init use '\${VMIMG}/seed.img' as a cloud-init seed -b, --background start in the background -g, --gdb launch under gdb -p, --perf perf record @@ -36,8 +35,8 @@ Options: event with a '-'" _run() { - local short="k:cd:bnfgpt:h" - local long="kernel-dir:,cloud-init,background,print,reset,gdb,perf,trace:,help" + local short="k:d:bnfgpt:h" + local long="kernel-dir:,background,print,reset,gdb,perf,trace:,help" if ! tmp=$(getopt -o "$short" --long "$long" -n "$BASENAME" -- "$@"); then exit 1 @@ -52,10 +51,6 @@ _run() { local kernel_dir="$2" ; shift 2 ;; - '-c' | '--cloud-init' ) - local do_cloud_init=1; shift - ;; - '-b' | '--background' ) local do_background=1; shift ;; @@ -127,17 +122,6 @@ _run() { truncate -s 0 "${VMLOG}/${logfile}" done - if [[ -v do_cloud_init ]]; then - if [[ ! -f "${VMIMG}/seed.img" ]]; then - _fatal 1 "no such file: '${VMIMG}/seed.img'" - fi - - qemu_drive_add "cloud-init-seed" \ - --file "img/seed.img" \ - --format "raw" \ - --interface "virtio" - fi - QEMU_PARAMS+=("-pidfile" "${VMROOT}/run/${VMNAME}/pidfile") if [[ -v kernel_dir ]]; then From 3f42a2af336644893bc82188faf97aa9ab97adb2 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Mon, 14 Jul 2025 23:09:35 +0200 Subject: [PATCH 06/10] contrib: remove cloud init specific scripts Signed-off-by: Joel Granados --- contrib/generate-cloud-config-seed-freebsd.sh | 45 -------- contrib/generate-cloud-config-seed.sh | 109 ------------------ .../systemd/mount-shared-kernel-dir.service | 71 ------------ 3 files changed, 225 deletions(-) delete mode 100755 contrib/generate-cloud-config-seed-freebsd.sh delete mode 100755 contrib/generate-cloud-config-seed.sh delete mode 100644 contrib/systemd/mount-shared-kernel-dir.service diff --git a/contrib/generate-cloud-config-seed-freebsd.sh b/contrib/generate-cloud-config-seed-freebsd.sh deleted file mode 100755 index 2742a38..0000000 --- a/contrib/generate-cloud-config-seed-freebsd.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: GPL-3.0-or-later - -set -euo pipefail - -cat </tmp/cloud-config -#cloud-config -disable_root: false - -users: - - name: vmuser - sudo: ALL=(ALL) NOPASSWD:ALL - lock_passwd: false - plain_text_passwd: 'vmuser' -EOF - -if [[ $# -gt 0 ]]; then - pubkey="$(<"$1")" - cat <>/tmp/cloud-config - ssh_authorized_keys: - - ${pubkey} - - name: root - ssh_authorized_keys: - - ${pubkey} -EOF -fi - -cat <>/tmp/cloud-config -write_files: -- path: /etc/ssh/sshd_config - content: | - PermitRootLogin yes - AuthorizedKeysFile .ssh/authorized_keys - Subsystem sftp /usr/libexec/sftp-server -EOF - -cat <>/tmp/cloud-config - -power_state: - mode: poweroff - condition: True -EOF - -cloud-localds -v seed.img /tmp/cloud-config -rm /tmp/cloud-config diff --git a/contrib/generate-cloud-config-seed.sh b/contrib/generate-cloud-config-seed.sh deleted file mode 100755 index 1db50fd..0000000 --- a/contrib/generate-cloud-config-seed.sh +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/env bash -# SPDX-License-Identifier: GPL-3.0-or-later - -set -euo pipefail - -cat </tmp/meta-data -instance-id: debian-1 -local-hostname: debian -EOF - -cat </tmp/user-data -#cloud-config -disable_root: false -ssh_pwauth: true -users: - - name: vmuser - sudo: ALL=(ALL) NOPASSWD:ALL - groups: users, admin - home: /home/vmuser - shell: /bin/bash - lock_passwd: false - plain_text_passwd: 'vmuser' -EOF - -if [[ $# -gt 0 ]]; then - pubkey="$(<"$1")" - cat <>/tmp/user-data - ssh_authorized_keys: - - ${pubkey} - - name: root - ssh_authorized_keys: - - ${pubkey} -EOF -fi - -cat <>/tmp/user-data - -write_files: -- path: /etc/systemd/system/mount-shared-kernel-dir.service - content: | - # MIT License - # - # Copyright (c) 2021 Omar Sandoval - # - # Permission is hereby granted, free of charge, to any person obtaining a copy - # of this software and associated documentation files (the "Software"), to deal - # in the Software without restriction, including without limitation the rights - # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - # copies of the Software, and to permit persons to whom the Software is furnished - # to do so, subject to the following conditions: - # - # The above copyright notice and this permission notice shall be included in - # all copies or substantial portions of the Software. - # - # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS - # OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF - # OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - # - # Cribbed and slightly modified from a systemd unit-file created by Omar - # Sandoval: - # - # https://github.com/osandov/osandov-linux/blob/master/scripts/vm-modules-mounter.service - # - - [Unit] - Description=Mount shared kernel build dir - DefaultDependencies=no - After=systemd-remount-fs.service - Before=local-fs-pre.target systemd-modules-load.service systemd-udevd.service kmod-static-nodes.service umount.target - Conflicts=umount.target - RefuseManualStop=true - ConditionPathExists=!/lib/modules/%v/kernel - - [Install] - WantedBy=local-fs-pre.target - - [Service] - Type=oneshot - RemainAfterExit=yes - ExecStart=mount -t tmpfs -o mode=755,strictatime,x-mount.mkdir,x-initrd.mount tmpfs /lib/modules/%v - ExecStart=mount -t 9p -o trans=virtio,ro,x-mount.mkdir,x-initrd.mount kernel_dir /lib/modules/%v/build - ExecStart=ln -s build/modules.order /lib/modules/%v/modules.order - ExecStart=ln -s build/modules.builtin /lib/modules/%v/modules.builtin - ExecStart=ln -s build /lib/modules/%v/kernel - ExecStart=-depmod %v - ExecStopPost=sh -c 'if mountpoint -q /lib/modules/%v/build; then umount -l /lib/modules/%v/build; fi' - ExecStopPost=sh -c 'if mountpoint -q /lib/modules/%v; then umount -l /lib/modules/%v; fi' - ExecStopPost=find /lib/modules -mindepth 1 -maxdepth 1 -type d -empty -delete - ExecReload=-depmod %v - -runcmd: - - [ systemctl, daemon-reload ] - - [ systemctl, enable, mount-shared-kernel-dir.service ] -EOF - -cat <>/tmp/user-data - -power_state: - mode: poweroff - condition: True -EOF - -#cloud-localds -v seed.img /tmp/cloud-config -mkisofs -output seed.img -volid cidata -joliet -rock /tmp/user-data /tmp/meta-data -rm /tmp/user-data -rm /tmp/meta-data diff --git a/contrib/systemd/mount-shared-kernel-dir.service b/contrib/systemd/mount-shared-kernel-dir.service deleted file mode 100644 index 05ea81c..0000000 --- a/contrib/systemd/mount-shared-kernel-dir.service +++ /dev/null @@ -1,71 +0,0 @@ -# MIT License -# -# Copyright (c) 2021 Omar Sandoval -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is furnished -# to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS -# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF -# OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# -# Cribbed and slightly modified from a systemd unit-file created by Omar -# Sandoval: -# -# https://github.com/osandov/osandov-linux/blob/master/scripts/vm-modules-mounter.service -# -# -# Linux kernel dir mounter service for systemd -# -# This systemd service will try to mount the p9 virtfs made available to the vm -# with the mount tag `kernel_dir` at very early boot. The network file system -# will be made available inside /lib/modules such that kernel modules can be -# loaded. -# -# Copy this file to /etc/systemd/system/mount-shared-kernel-dir.service and run -# -# systemctl daemon-reload -# systemctl enable mount-shared-kernel-dir.service -# -# Then reboot with the vmctl `--kernel-dir` option. -# - -[Unit] -Description=Mount shared kernel build dir -DefaultDependencies=no -After=systemd-remount-fs.service -Before=local-fs-pre.target systemd-modules-load.service systemd-udevd.service kmod-static-nodes.service umount.target -Conflicts=umount.target -RefuseManualStop=true -ConditionPathExists=!/lib/modules/%v/kernel - -[Install] -WantedBy=local-fs-pre.target - -[Service] -Type=oneshot -RemainAfterExit=yes -# Mount with x-initrd.mount so that systemd will ignore these mounts, because -# we want to unmount them ourselves. -ExecStart=/bin/mount -t tmpfs -o mode=755,strictatime,x-mount.mkdir,x-initrd.mount tmpfs /lib/modules/%v -ExecStart=/bin/mount -t 9p -o trans=virtio,ro,x-mount.mkdir,x-initrd.mount kernel_dir /lib/modules/%v/build -ExecStart=/bin/ln -s build/modules.order /lib/modules/%v/modules.order -ExecStart=/bin/ln -s build/modules.builtin /lib/modules/%v/modules.builtin -ExecStart=/bin/ln -s build /lib/modules/%v/kernel -ExecStart=/bin/depmod %v -# Lazy unmount to deal with stuff like udevd which keeps the mount busy. -ExecStopPost=/bin/sh -c 'if mountpoint -q /lib/modules/%v/build; then umount -l /lib/modules/%v/build; fi' -ExecStopPost=/bin/sh -c 'if mountpoint -q /lib/modules/%v; then umount -l /lib/modules/%v; fi' -ExecStopPost=/usr/bin/find /lib/modules -mindepth 1 -maxdepth 1 -type d -empty -delete -ExecReload=/bin/depmod %v - From 0fb3b875abeb1a380c951fa155481b15d32d360f Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Tue, 15 Jul 2025 08:55:14 +0200 Subject: [PATCH 07/10] cloudinit: Add all sshkey logic in a lib Signed-off-by: Joel Granados --- Makefile | 2 +- cmd/run | 2 +- examples/vm/cloudinit.conf | 28 ++++------------------ lib/cloudinit/sshkey | 49 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 25 deletions(-) create mode 100644 lib/cloudinit/sshkey diff --git a/Makefile b/Makefile index bb63579..b478ba4 100644 --- a/Makefile +++ b/Makefile @@ -6,5 +6,5 @@ ifndef SHELLCHECK $(error "cannot find shellcheck; install to run check") endif shellcheck -a -x \ - vmctl cmd/* common/* lib/qemu/* + vmctl cmd/* common/* lib/qemu/* lib/cloudinit/* diff --git a/cmd/run b/cmd/run index a4d3fcb..d6b2aba 100644 --- a/cmd/run +++ b/cmd/run @@ -9,7 +9,7 @@ set -euo pipefail #shellcheck source=common/rc source "${BASEDIR}/common/rc" -for rc in "${BASEDIR}"/lib/qemu/*; do +for rc in "${BASEDIR}"/lib/*/*; do #shellcheck disable=SC1090 source "$rc" done diff --git a/examples/vm/cloudinit.conf b/examples/vm/cloudinit.conf index fa9dbd5..ba7f043 100644 --- a/examples/vm/cloudinit.conf +++ b/examples/vm/cloudinit.conf @@ -3,7 +3,7 @@ CLOUD_INIT_STAGE_DIR="$(mktemp -d)" trap 'rm -rfv ${CLOUD_INIT_STAGE_DIR}' EXIT -#CLOUD_INIT_SSHKEY="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFAKEKEYPLACEHOLDER ubuntu@placeholder" +#CLOUDINIT_SSHKEY="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFAKEKEYPLACEHOLDER ubuntu@placeholder" CLOUD_INIT_STAGE_FILE="${CLOUD_INIT_STAGE_DIR}/seed.img" CLOUD_INIT_OUTPUT_IMG="img/base.qcow2" GUEST_BOOT_BASE="${CLOUD_INIT_STAGE_DIR}/base.qcow2" @@ -21,28 +21,10 @@ cloudinit_resize_img() { qemu-img resize "${cloud_img_path}" 8G } -cloudinit_get_sshkey() { - if [[ -v CLOUD_INIT_SSHKEY ]]; then - echo "${CLOUD_INIT_SSHKEY}" - return 0 - fi - - SSH_KEY_FILE=$(find ~/.ssh -maxdepth 1 -type f -name "*.pub" | head -n 1) - - if [[ -z "$SSH_KEY_FILE" ]]; then - _fatal 1 "No public key found in ~/.ssh. Try setting CLOUD_INIT_SSHKEY." - fi - - # Read the key content - CLOUD_INIT_SSHKEY=$(cat "$SSH_KEY_FILE") - echo "${CLOUD_INIT_SSHKEY}" - return 0 -} - cloudinit_make_userdatafile() { local usr_data_file="$1" - local ssh_key="" - ssh_key="$(cloudinit_get_sshkey)" + + cloudinit_get_sshkey cat > "${usr_data_file}" << EOF #cloud-config @@ -58,11 +40,11 @@ users: lock_passwd: false plain_text_passwd: 'vmuser' ssh_authorized_keys: - - ${ssh_key} + - ${CLOUDINIT_SSHKEY} - name: root ssh_authorized_keys: - - ${ssh_key} + - ${CLOUDINIT_SSHKEY} write_files: - path: /etc/systemd/system/mount-shared-kernel-dir.service diff --git a/lib/cloudinit/sshkey b/lib/cloudinit/sshkey new file mode 100644 index 0000000..4f1122e --- /dev/null +++ b/lib/cloudinit/sshkey @@ -0,0 +1,49 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: GPL-3.0-or-later +# Copyright (c) 2025 Samsung Electronics Co., Ltd. All Rights Reserved. +# +# Written by Joel Granados + +cloudinit_get_sshkey() { + local long="key-file:" + + if ! tmp=$(getopt -o "" --long "$long" -n "${FUNCNAME[0]}" -- "$@"); then + exit 1 + fi + + eval set -- "$tmp" + unset tmp + + while true; do + case "$1" in + '--key-file' ) + local key_file="$2"; shift 2 + ;; + '--' ) + shift; break + ;; + * ) + _fatal 1 "unknown argument '$1'" + ;; + esac + done + + if [[ -v CLOUDINIT_SSHKEY && -n "${CLOUDINIT_SSHKEY}" ]]; then + return + fi + + if [[ ! -v key_file ]]; then + local key_file="" + key_file="$(find ~/.ssh -maxdepth 1 -type f -name "*.pub" | head -n 1)" + if [[ -z "${key_file}" ]]; then + _fatal 1 "No public key found in ~/.ssh. Try setting CLOUDINIT_SSHKEY." + fi + fi + + if [[ ! -f "${key_file}" ]]; then + _fatal 1 "File not found ${key_file}" + fi + + CLOUDINIT_SSHKEY=$(cat "${key_file}") +} + From 220f3f7c84a1d8f69db3a3cfa2a83ba257c320b7 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Tue, 15 Jul 2025 10:02:36 +0200 Subject: [PATCH 08/10] cloudinit: Add seed image creation to a lib Signed-off-by: Joel Granados --- examples/vm/cloudinit.conf | 121 ++++++---------------------------- lib/cloudinit/seedimg | 130 +++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+), 101 deletions(-) create mode 100644 lib/cloudinit/seedimg diff --git a/examples/vm/cloudinit.conf b/examples/vm/cloudinit.conf index ba7f043..a9a7eb7 100644 --- a/examples/vm/cloudinit.conf +++ b/examples/vm/cloudinit.conf @@ -1,12 +1,10 @@ #!/usr/bin/env bash -CLOUD_INIT_STAGE_DIR="$(mktemp -d)" -trap 'rm -rfv ${CLOUD_INIT_STAGE_DIR}' EXIT +CLOUDINIT_STAGE_DIR="$(mktemp -d)" +trap 'rm -rfv ${CLOUDINIT_STAGE_DIR}' EXIT -#CLOUDINIT_SSHKEY="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFAKEKEYPLACEHOLDER ubuntu@placeholder" -CLOUD_INIT_STAGE_FILE="${CLOUD_INIT_STAGE_DIR}/seed.img" -CLOUD_INIT_OUTPUT_IMG="img/base.qcow2" -GUEST_BOOT_BASE="${CLOUD_INIT_STAGE_DIR}/base.qcow2" +#CLOUDINIT_SSHKEY="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFAKEKEYPLACEHOLDER vmuser@placeholder" +GUEST_BOOT_BASE="${CLOUDINIT_STAGE_DIR}/base.qcow2" source "x86_64-q35-base.conf" cloudinit_wget_ubuntu() { @@ -21,117 +19,38 @@ cloudinit_resize_img() { qemu-img resize "${cloud_img_path}" 8G } -cloudinit_make_userdatafile() { - local usr_data_file="$1" - - cloudinit_get_sshkey - - cat > "${usr_data_file}" << EOF -#cloud-config -disable_root: false -ssh_pwauth: false - -users: - - name: vmuser - sudo: ['ALL=(ALL) NOPASSWD:ALL'] - groups: users, admin - home: /home/vmuser - shell: /bin/bash - lock_passwd: false - plain_text_passwd: 'vmuser' - ssh_authorized_keys: - - ${CLOUDINIT_SSHKEY} - - - name: root - ssh_authorized_keys: - - ${CLOUDINIT_SSHKEY} - -write_files: -- path: /etc/systemd/system/mount-shared-kernel-dir.service - content: | - # Cribbed and slightly modified from a systemd unit-file created by Omar - # Sandoval: - # https://github.com/osandov/osandov-linux/blob/master/scripts/vm-modules-mounter.service - - [Unit] - Description=Mount shared kernel build dir - DefaultDependencies=no - After=systemd-remount-fs.service - Before=local-fs-pre.target systemd-modules-load.service systemd-udevd.service kmod-static-nodes.service umount.target - Conflicts=umount.target - RefuseManualStop=true - ConditionPathExists=!/lib/modules/%v/kernel - - [Install] - WantedBy=local-fs-pre.target - - [Service] - Type=oneshot - RemainAfterExit=yes - ExecStart=mount -t tmpfs -o mode=755,strictatime,x-mount.mkdir,x-initrd.mount tmpfs /lib/modules/%v - ExecStart=mount -t 9p -o trans=virtio,ro,x-mount.mkdir,x-initrd.mount kernel_dir /lib/modules/%v/build - ExecStart=ln -s build/modules.order /lib/modules/%v/modules.order - ExecStart=ln -s build/modules.builtin /lib/modules/%v/modules.builtin - ExecStart=ln -s build /lib/modules/%v/kernel - ExecStart=-depmod %v - ExecStopPost=sh -c 'if mountpoint -q /lib/modules/%v/build; then umount -l /lib/modules/%v/build; fi' - ExecStopPost=sh -c 'if mountpoint -q /lib/modules/%v; then umount -l /lib/modules/%v; fi' - ExecStopPost=find /lib/modules -mindepth 1 -maxdepth 1 -type d -empty -delete - ExecReload=-depmod %v - -runcmd: - - [ systemctl, daemon-reload ] - - [ systemctl, enable, mount-shared-kernel-dir.service ] - -power_state: - mode: poweroff - condition: true -EOF -} - -cloudinit_make_metadatafile() { - local meta_data_file="$1" - - cat > "${meta_data_file}" << EOF -#cloud-config -#instance-id: vmctl-1 -#local-hostname: vmctl -EOF -} - -cloudinit_make_seed_img() { - local seed_img_path="$1" - local usr_data_file="${CLOUD_INIT_STAGE_DIR}/user-data" - local meta_data_file="${CLOUD_INIT_STAGE_DIR}/meta-data" - - cloudinit_make_userdatafile "${usr_data_file}" - cloudinit_make_metadatafile "${meta_data_file}" - - mkisofs -output "${seed_img_path}" \ - -volid cidata -joliet -rock "${usr_data_file}" "${meta_data_file}" -} - _pre() { _require_program mkisofs _require_program wget local cloud_img="${GUEST_BOOT}" - if [[ -f "${CLOUD_INIT_OUTPUT_IMG}" ]]; then - _fatal 1 "${CLOUD_INIT_OUTPUT_IMG} exist; rename to avoid overwriting" + if [[ -f "img/base.qcow2" ]]; then + _fatal 1 "img/base.qcow2 exist; rename to avoid overwriting" fi cloudinit_wget_ubuntu "${cloud_img}" cloudinit_resize_img "${cloud_img}" - cloudinit_make_seed_img "${CLOUD_INIT_STAGE_FILE}" + + qemu-img resize "${cloud_img}" 8G + + local meta_data_file="${CLOUDINIT_STAGE_DIR}/meta-data" + local usr_data_file="${CLOUDINIT_STAGE_DIR}/user-data" + + cloudinit_make_seed_img \ + --seed-img-path "${CLOUDINIT_STAGE_DIR}/seed.img" \ + --meta-data-file "${meta_data_file}" \ + --usr-data-file "${usr_data_file}" \ + --usr-name "vmuser" \ + --usr-passwd "vmuser" } _setup_cloudinit() { _setup_x86_64_q35_base - QEMU_PARAMS+=("-drive" "id=cloud-init-seed,file=${CLOUD_INIT_STAGE_FILE},format=raw,media=cdrom") + QEMU_PARAMS+=("-drive" "id=cloud-init-seed,file=${CLOUDINIT_STAGE_DIR}/seed.img,format=raw,media=cdrom") } _post() { mkdir -p "img" - mv "${GUEST_BOOT}" "${CLOUD_INIT_OUTPUT_IMG}" + mv "${GUEST_BOOT}" "img/base.qcow2" } diff --git a/lib/cloudinit/seedimg b/lib/cloudinit/seedimg new file mode 100644 index 0000000..ff2291a --- /dev/null +++ b/lib/cloudinit/seedimg @@ -0,0 +1,130 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: GPL-3.0-or-later +# Copyright (c) 2025 Samsung Electronics Co., Ltd. All Rights Reserved. +# +# Written by Joel Granados + +cloudinit_make_userdatafile() { + cloudinit_get_sshkey + _require_vars "CLOUDINIT_SSHKEY" + + cat > "${CLOUDINIT_USR_DATA_FILE}" << EOF +#cloud-config +disable_root: false +ssh_pwauth: false + +users: + - name: ${CLOUDINIT_USR_NAME} + sudo: ['ALL=(ALL) NOPASSWD:ALL'] + groups: users, admin + home: /home/${CLOUDINIT_USR_NAME} + shell: /bin/bash + lock_passwd: false + plain_text_passwd: '${CLOUDINIT_USR_PASSWD}' + ssh_authorized_keys: + - ${CLOUDINIT_SSHKEY} + + - name: root + ssh_authorized_keys: + - ${CLOUDINIT_SSHKEY} + +write_files: +- path: /etc/systemd/system/mount-shared-kernel-dir.service + content: | + # Cribbed and slightly modified from a systemd unit-file created by Omar Sandoval: + # https://github.com/osandov/osandov-linux/blob/master/scripts/vm-modules-mounter.service + + [Unit] + Description=Mount shared kernel build dir + DefaultDependencies=no + After=systemd-remount-fs.service + Before=local-fs-pre.target systemd-modules-load.service systemd-udevd.service kmod-static-nodes.service umount.target + Conflicts=umount.target + RefuseManualStop=true + ConditionPathExists=!/lib/modules/%v/kernel + + [Install] + WantedBy=local-fs-pre.target + + [Service] + Type=oneshot + RemainAfterExit=yes + ExecStart=mount -t tmpfs -o mode=755,strictatime,x-mount.mkdir,x-initrd.mount tmpfs /lib/modules/%v + ExecStart=mount -t 9p -o trans=virtio,ro,x-mount.mkdir,x-initrd.mount kernel_dir /lib/modules/%v/build + ExecStart=ln -s build/modules.order /lib/modules/%v/modules.order + ExecStart=ln -s build/modules.builtin /lib/modules/%v/modules.builtin + ExecStart=ln -s build /lib/modules/%v/kernel + ExecStart=-depmod %v + ExecStopPost=sh -c 'if mountpoint -q /lib/modules/%v/build; then umount -l /lib/modules/%v/build; fi' + ExecStopPost=sh -c 'if mountpoint -q /lib/modules/%v; then umount -l /lib/modules/%v; fi' + ExecStopPost=find /lib/modules -mindepth 1 -maxdepth 1 -type d -empty -delete + ExecReload=-depmod %v + +runcmd: + - [ systemctl, daemon-reload ] + - [ systemctl, enable, mount-shared-kernel-dir.service ] + +power_state: + mode: poweroff + condition: true +EOF +} + +cloudinit_make_metadatafile() { + cat > "${CLOUDINIT_META_DATA_FILE}" << EOF +#cloud-config +#instance-id: vmctl-1 +#local-hostname: vmctl +EOF +} + +cloudinit_make_seed_img() { + local long="seed-img-path:,meta-data-file:,usr-data-file:,usr-name:,usr-passwd:" + + if ! tmp=$(getopt -o "" --long "$long" -n "${FUNCNAME[0]}" -- "$@"); then + exit 1 + fi + + eval set -- "$tmp" + unset tmp + + CLOUDINIT_USR_NAME="vmuser" + CLOUDINIT_USR_PASSWD="${CLOUDINIT_USR_NAME}" + while true; do + case "$1" in + '--meta-data-file' ) + CLOUDINIT_META_DATA_FILE="$2"; shift 2 + ;; + '--usr-data-file' ) + CLOUDINIT_USR_DATA_FILE="$2"; shift 2 + ;; + '--usr-name' ) + CLOUDINIT_USR_NAME="$2"; shift 2 + ;; + '--usr-passwd' ) + CLOUDINIT_USR_PASSWD="$2"; shift 2; + ;; + '--seed-img-path' ) + local seed_img_path="$2"; shift 2; + ;; + '--' ) + shift; break + ;; + * ) + _fatal 1 "unknown argument '$1'" + ;; + esac + done + + if [[ ! -v seed_img_path ]]; then + _fatal 1 "${FUNCNAME[0]} Missing --seed-img-path arg" + fi + + _require_vars "CLOUDINIT_META_DATA_FILE CLOUDINIT_USR_DATA_FILE" + + cloudinit_make_userdatafile + cloudinit_make_metadatafile + + mkisofs -output "${seed_img_path}" \ + -volid cidata -joliet -rock "${CLOUDINIT_USR_DATA_FILE}" "${CLOUDINIT_META_DATA_FILE}" +} From 14a5d2697fd804f68e109c95d75ec58b317b1fe8 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Tue, 15 Jul 2025 10:05:39 +0200 Subject: [PATCH 09/10] cloudinit: Remove cloud_img var from cloudinit config Signed-off-by: Joel Granados --- examples/vm/cloudinit.conf | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/vm/cloudinit.conf b/examples/vm/cloudinit.conf index a9a7eb7..b1ce1ef 100644 --- a/examples/vm/cloudinit.conf +++ b/examples/vm/cloudinit.conf @@ -22,16 +22,16 @@ cloudinit_resize_img() { _pre() { _require_program mkisofs _require_program wget - local cloud_img="${GUEST_BOOT}" + _require_vars "GUEST_BOOT" if [[ -f "img/base.qcow2" ]]; then _fatal 1 "img/base.qcow2 exist; rename to avoid overwriting" fi - cloudinit_wget_ubuntu "${cloud_img}" - cloudinit_resize_img "${cloud_img}" + cloudinit_wget_ubuntu "${GUEST_BOOT}" + cloudinit_resize_img "${GUEST_BOOT}" - qemu-img resize "${cloud_img}" 8G + qemu-img resize "${GUEST_BOOT}" 8G local meta_data_file="${CLOUDINIT_STAGE_DIR}/meta-data" local usr_data_file="${CLOUDINIT_STAGE_DIR}/user-data" From a18956a6a201fc5b24b5cc02829f626a7447e7f1 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Tue, 15 Jul 2025 10:34:53 +0200 Subject: [PATCH 10/10] cloudinit: Put all img admin logic in a lib Signed-off-by: Joel Granados --- examples/vm/cloudinit.conf | 28 +++++------------- lib/cloudinit/cimgs | 58 ++++++++++++++++++++++++++++++++++++++ lib/cloudinit/seedimg | 1 + 3 files changed, 66 insertions(+), 21 deletions(-) create mode 100644 lib/cloudinit/cimgs diff --git a/examples/vm/cloudinit.conf b/examples/vm/cloudinit.conf index b1ce1ef..272982a 100644 --- a/examples/vm/cloudinit.conf +++ b/examples/vm/cloudinit.conf @@ -1,40 +1,26 @@ #!/usr/bin/env bash CLOUDINIT_STAGE_DIR="$(mktemp -d)" +GUEST_BOOT_BASE="${CLOUDINIT_STAGE_DIR}/base.qcow2" trap 'rm -rfv ${CLOUDINIT_STAGE_DIR}' EXIT -#CLOUDINIT_SSHKEY="ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFAKEKEYPLACEHOLDER vmuser@placeholder" -GUEST_BOOT_BASE="${CLOUDINIT_STAGE_DIR}/base.qcow2" source "x86_64-q35-base.conf" -cloudinit_wget_ubuntu() { - local cloud_img_path="$1" - local uimg="https://cloud-images.ubuntu.com/releases/plucky/release" - uimg="${uimg}/ubuntu-25.04-server-cloudimg-amd64.img" - wget -O "${cloud_img_path}" "${uimg}" -} - -cloudinit_resize_img() { - local cloud_img_path="$1" - qemu-img resize "${cloud_img_path}" 8G -} - _pre() { _require_program mkisofs _require_program wget _require_vars "GUEST_BOOT" + local meta_data_file="${CLOUDINIT_STAGE_DIR}/meta-data" + local usr_data_file="${CLOUDINIT_STAGE_DIR}/user-data" + if [[ -f "img/base.qcow2" ]]; then _fatal 1 "img/base.qcow2 exist; rename to avoid overwriting" fi - cloudinit_wget_ubuntu "${GUEST_BOOT}" - cloudinit_resize_img "${GUEST_BOOT}" - - qemu-img resize "${GUEST_BOOT}" 8G - - local meta_data_file="${CLOUDINIT_STAGE_DIR}/meta-data" - local usr_data_file="${CLOUDINIT_STAGE_DIR}/user-data" + cloudinit_get_img --ubuntu-25 \ + --cloud-img-path "${GUEST_BOOT}" \ + --resize-to 8G cloudinit_make_seed_img \ --seed-img-path "${CLOUDINIT_STAGE_DIR}/seed.img" \ diff --git a/lib/cloudinit/cimgs b/lib/cloudinit/cimgs new file mode 100644 index 0000000..494eefd --- /dev/null +++ b/lib/cloudinit/cimgs @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: GPL-3.0-or-later +# Copyright (c) 2025 Samsung Electronics Co., Ltd. All Rights Reserved. +# +# Written by Joel Granados + +cloudinit_get_img() { + _require_program wget + + local long="ubuntu-25,cloud-img-path:,resize-to:" + + if ! tmp=$(getopt -o "" --long "$long" -n "${FUNCNAME[0]}" -- "$@"); then + exit 1 + fi + + eval set -- "$tmp" + unset tmp + + while true; do + case "$1" in + '--ubuntu-25' ) + local base_url="https://cloud-images.ubuntu.com/releases/plucky/release" + local img_name="ubuntu-25.04-server-cloudimg-amd64.img" + shift + ;; + '--cloud-img-path' ) + local cloud_img_path="$2"; shift 2 + ;; + '--resize-to' ) + local resize_to="$2"; shift 2 + ;; + '--' ) + shift; break + ;; + * ) + _fatal 1 "unknown argument '$1'" + ;; + esac + done + + if [[ ! -v base_url || ! -v img_name ]]; then + _fatal 1 "${FUNCNAME[0]} base_url or img_name are undefined" + fi + + if [[ ! -v cloud_img_path ]]; then + _fatal 1 "${FUNCNAME[0]} missing --cloud-img-path arg" + fi + + if [[ -v resize_to ]]; then + _require_program qemu-img + fi + + wget -O "${cloud_img_path}" "${base_url}/${img_name}" + + if [[ -v resize_to ]]; then + qemu-img resize "${cloud_img_path}" "${resize_to}" + fi +} diff --git a/lib/cloudinit/seedimg b/lib/cloudinit/seedimg index ff2291a..37c33f3 100644 --- a/lib/cloudinit/seedimg +++ b/lib/cloudinit/seedimg @@ -79,6 +79,7 @@ EOF } cloudinit_make_seed_img() { + _require_program mkisofs local long="seed-img-path:,meta-data-file:,usr-data-file:,usr-name:,usr-passwd:" if ! tmp=$(getopt -o "" --long "$long" -n "${FUNCNAME[0]}" -- "$@"); then