summaryrefslogtreecommitdiff
path: root/bin/debvm-create
diff options
context:
space:
mode:
authorHelmut Grohne <helmut@subdivi.de>2023-01-11 19:25:08 +0100
committerHelmut Grohne <helmut@subdivi.de>2023-01-11 19:25:08 +0100
commitaece2dad3e16feb6073e17a3b61dfd8d69007ecb (patch)
treedb5608dbf49e9a8d1c54a610036024c99b36d489 /bin/debvm-create
parent80b239b821af73f8a74b3694cc116773d37d43d6 (diff)
downloaddebvm-aece2dad3e16feb6073e17a3b61dfd8d69007ecb.tar.gz
move the debvm-* tools to bin
The purpose of this change is adding support files to be referenced and called from these tools. Those support files shall be located in ../share and this way of locating them shall work both in-source and when installed.
Diffstat (limited to 'bin/debvm-create')
-rwxr-xr-xbin/debvm-create342
1 files changed, 342 insertions, 0 deletions
diff --git a/bin/debvm-create b/bin/debvm-create
new file mode 100755
index 0000000..0dc619b
--- /dev/null
+++ b/bin/debvm-create
@@ -0,0 +1,342 @@
+#!/bin/sh
+# Copyright 2022 Helmut Grohne <helmut@subdivi.de>
+# SPDX-License-Identifier: MIT
+
+# shellcheck disable=SC2016 # Intentional quoting technique
+
+: <<'POD2MAN'
+=head1 NAME
+
+debvm-create - Create a VM image for various Debian releases and architectures
+
+=head1 SYNOPSIS
+
+B<debvm-create> [B<-a> I<architecture>] [B<-h> I<hostname>] [B<-k> F<sshkey>] [B<-m> I<mirror>] [B<-o> F<output>] [B<-p> I<package>] [B<-r> I<release>] [B<-z> I<size_in_GB>] [B<--> I<mmdebstrap options>]
+
+=head1 DESCRIPTION
+
+B<debvm-create> is essentially a thin wrapper around B<mmdebstrap> for creating a raw ext4 filesystem image for booting with B<debvm-run>.
+The purpose of these images primarily is testing the different releases and architectures without access to a physical machine of that architecture.
+Beyond essential packages, the image will contain B<apt>, an init system and a suitable kernel package.
+Notably absent is a bootloader and a partition table.
+In order to boot such an image, one is supposed to extract the kernel and initrd from the image and pass it to a suitable bootloader.
+No user account is created and root can login without specifying a password.
+
+=head1 OPTIONS
+
+=over 8
+
+=item B<-a> I<architecture>, B<--architecture>=I<architecture>
+
+Specify a Debian architecture name.
+By default, the native architecture is being used.
+A suitable kernel image is automatically selected and installed into the image.
+
+=item B<-h> I<hostname>, B<--hostname>=I<hostname>
+
+Set the hostname of the virtual machine.
+By default, the hostname is B<testvm>.
+
+=item B<-k> F<sshkey>, B<--sshkey>=F<sshkey>
+
+Install the given ssh public key file into the virtual machine image for the root user.
+This option also causes the ssh server to be installed.
+By default, no key or server is installed.
+To connect to the vm, pass a port number to B<debvm-run> with the B<-s> option.
+
+=item B<-m> I<mirror>, B<--mirror>=I<mirror>
+
+Specify the Debian mirror to be used for downloading packages and to be configured inside the virtual machine image.
+By default, L<http://deb.debian.org/debian> is being used.
+
+=item B<-o> F<output>, B<--output>=F<output>
+
+Specify the file name of the resulting virtual machine image.
+By default, it is written to F<rootfs.ext4>.
+
+=item B<-p> I<package>, B<--package>=I<package>
+
+Request additional packages to be installed into the virtual machine image.
+This option can be specified multiple times and packages can be separated by a comma.
+Package recommendations are not honoured.
+If a linux-image is passed here, it will replace the one selected by default.
+
+=item B<-r> I<release>, B<--release>=I<release>
+
+Use the given Debian release.
+By default, B<unstable> is being used.
+
+=item B<-z> I<size_in_GB>, B<--size>=I<size_in_GB>
+
+Specify the minimum image size in giga bytes.
+The resulting image will be grown as a sparse file to this size if necessary.
+The default is 1 GB.
+
+=item B<--> I<mmdebstrap options>
+
+All options beyond a double dash are passed to B<mmdebstrap> before the suite, target and mirror specification.
+This can be used to provide additional hooks for image customization.
+
+=back
+
+=head1 SEE ALSO
+
+ debvm-run(1) mmdebstrap(1)
+
+=cut
+POD2MAN
+
+set -u
+
+ARCHITECTURE=$(dpkg --print-architecture)
+IMAGE=rootfs.ext4
+INCLUDE_PACKAGES=init
+MIRROR="http://deb.debian.org/debian"
+SIZE=$((1024*1024*1024))
+SSHKEY=
+SUITE=unstable
+VMNAME=testvm
+
+nth_arg() {
+ shift "$1"
+ printf "%s" "$1"
+}
+
+die() {
+ echo "$*" 1>&2
+ exit 1
+}
+usage() {
+ die "usage: $0 [-a architecture] [-h hostname] [-k sshkey] [-m mirror] [-o output] [-p packages] [-r release] [-z size_in_GB] [-- mmdebstrap options]"
+}
+usage_error() {
+ echo "error: $*" 1>&2
+ usage
+}
+
+opt_architecture() {
+ ARCHITECTURE=$1
+}
+opt_hostname() {
+ VMNAME=$1
+}
+opt_mirror() {
+ MIRROR=$1
+}
+opt_sshkey() {
+ SSHKEY=$1
+}
+opt_output() {
+ IMAGE=$1
+}
+opt_package() {
+ INCLUDE_PACKAGES="$INCLUDE_PACKAGES,$1"
+}
+opt_release() {
+ SUITE=$1
+}
+opt_size() {
+ SIZE=$(($1*1024*1024*1024))
+}
+
+while getopts :a:h:k:m:o:p:r:z:-: OPTCHAR; do
+ case "$OPTCHAR" in
+ a) opt_architecture "$OPTARG" ;;
+ h) opt_hostname "$OPTARG" ;;
+ k) opt_sshkey "$OPTARG" ;;
+ m) opt_mirror "$OPTARG" ;;
+ o) opt_output "$OPTARG" ;;
+ p) opt_package "$OPTARG" ;;
+ r) opt_release "$OPTARG" ;;
+ z) opt_size "$OPTARG" ;;
+ -)
+ case "$OPTARG" in
+ help)
+ usage
+ ;;
+ architecture|hostname|mirror|output|package|release|size|sshkey)
+ test "$OPTIND" -gt "$#" && usage_error "missing argument for --$OPTARG"
+ "opt_$OPTARG" "$(nth_arg "$OPTIND" "$@")"
+ OPTIND=$((OPTIND+1))
+ ;;
+ architecture=*|hostname=*|mirror=*|output=*|package=*|release=*|size=*|sshkey=*)
+ "opt_${OPTARG%%=*}" "${OPTARG#*=}"
+ ;;
+ *)
+ usage_error "unrecognized option --$OPTARG"
+ ;;
+ esac
+ ;;
+ :)
+ usage_error "missing argument for -$OPTARG"
+ ;;
+ '?')
+ usage_error "unrecognized option -$OPTARG"
+ ;;
+ *)
+ die "internal error while parsing command options, please report a bug"
+ ;;
+ esac
+done
+shift "$((OPTIND - 1))"
+
+if test -n "$SSHKEY" && ! test -f "$SSHKEY"; then
+ die "error: ssh keyfile '$SSHKEY' not found"
+fi
+
+case "$SUITE" in
+ jessie)
+ DEBVER=8
+ ;;
+ stretch)
+ DEBVER=9
+ ;;
+ buster)
+ DEBVER=10
+ ;;
+ bullseye|stable)
+ DEBVER=11
+ ;;
+ bookworm|testing)
+ DEBVER=12
+ ;;
+ trixie)
+ DEBVER=13
+ ;;
+ forky)
+ DEBVER=14
+ ;;
+ sid|unstable)
+ DEBVER=999
+ ;;
+ *)
+ die "unrecognized Debian release: $SUITE"
+ ;;
+esac
+
+KERNEL_SUFFIX=-$ARCHITECTURE
+case "$ARCHITECTURE" in
+ amd64|arm64)
+ KERNEL_SUFFIX="-cloud-$ARCHITECTURE"
+ if test "$DEBVER" -le 9; then
+ KERNEL_SUFFIX="-$ARCHITECTURE"
+ fi
+ ;;
+ armhf)
+ KERNEL_SUFFIX=-armmp
+ ;;
+ i386)
+ KERNEL_SUFFIX=-686-pae
+ ;;
+ mips64el)
+ KERNEL_SUFFIX=-5kc-malta
+ ;;
+ mipsel)
+ KERNEL_SUFFIX=-4kc-malta
+ ;;
+ ppc64el)
+ KERNEL_SUFFIX=-powerpc64le
+ ;;
+esac
+
+case ",$INCLUDE_PACKAGES," in
+ *,linux-image-*)
+ ;;
+ *)
+ INCLUDE_PACKAGES="$INCLUDE_PACKAGES,linux-image$KERNEL_SUFFIX"
+ ;;
+esac
+
+if test -n "$SSHKEY"; then
+ INCLUDE_PACKAGES="$INCLUDE_PACKAGES,openssh-server"
+fi
+
+# add a DNS resolver
+if test "$DEBVER" -ge 9; then
+ INCLUDE_PACKAGES="$INCLUDE_PACKAGES,libnss-resolve"
+fi
+if test "$DEBVER" -le 11; then
+ set -- '--customize-hook=chroot "$1" systemctl enable systemd-resolved.service' "$@"
+fi
+if test "$DEBVER" -le 9; then
+ set -- '--customize-hook=ln -fs ../run/systemd/resolve/resolv.conf "$1/etc/resolv.conf"' "$@"
+elif test "$DEBVER" -le 11; then
+ set -- '--customize-hook=ln -fs ../run/systemd/resolve/stub-resolv.conf "$1/etc/resolv.conf"' "$@"
+fi
+
+# construct mmdebstrap options as $@:
+set -- \
+ --verbose \
+ --variant=apt \
+ --format=ext2 \
+ "--architecture=$ARCHITECTURE" \
+ "--include=$INCLUDE_PACKAGES" \
+ '--customize-hook=echo "LABEL=debvm / ext4 defaults 0 1" >"$1/etc/fstab"' \
+ "$@"
+
+
+# set up a hostname
+set -- \
+ "--customize-hook=echo $VMNAME >"'"$1/etc/hostname"' \
+ "--customize-hook=echo 127.0.0.1 localhost $VMNAME >"'"$1/etc/hosts"' \
+ "$@"
+
+# allow password-less root login
+set -- '--customize-hook=chroot "$1" passwd --delete root' "$@"
+
+# dhcp on all network interfaces
+SYSD_NET_MATCH='Name=en*\n'
+test "$DEBVER" -le 8 && SYSD_NET_MATCH="${SYSD_NET_MATCH}Name=eth*\\n"
+SYSD_NET_NET='DHCP=yes\n'
+# This anchor is included by default since bullseye. Fails DNSSEC validation when missing.
+test "$DEBVER" -le 11 && SYSD_NET_NET="${SYSD_NET_NET}DNSSECNegativeTrustAnchors=home.arpa\\n"
+set -- \
+ '--customize-hook=chroot "$1" systemctl enable systemd-networkd.service' \
+ "--customize-hook=printf \"[Match]\\n$SYSD_NET_MATCH\\n[Network]\\n$SYSD_NET_NET"'\n[DHCP]\nUseDomains=yes\n" > "$1/etc/systemd/network/20-wired.network"' \
+ "$@"
+
+# add ssh key for root
+if test -n "$SSHKEY"; then
+ set -- \
+ '--customize-hook=mkdir -m700 -p "$1/root/.ssh"' \
+ "--customize-hook=upload $SSHKEY /root/.ssh/authorized_keys" \
+ "$@"
+fi
+
+set -- --skip=cleanup/apt/lists "$@"
+
+# We need /var/lib/dpkg/available for dpkg --set-selections to work.
+set -- '--customize-hook=chroot "$1" apt-cache dumpavail | chroot "$1" dpkg --update-avail' "$@"
+
+if test "$DEBVER" -le 8; then
+ # Use obsolete and expired keys.
+ set -- '--keyring=/usr/share/keyrings/debian-archive-removed-keys.gpg' "$@"
+ set -- --aptopt='Apt::Key::gpgvcommand "/usr/libexec/mmdebstrap/gpgvnoexpkeysig"' "$@"
+ set -- --hook-dir=/usr/share/mmdebstrap/hooks/jessie-or-older "$@"
+fi
+
+if test "$DEBVER" -ge 12; then
+ # Avoid the usrmerge package
+ set -- --hook-dir=/usr/share/mmdebstrap/hooks/merged-usr "$@"
+fi
+
+set -- \
+ '--customize-hook=mkdir "$1/etc/systemd/system/serial-getty@.service.d"' \
+ "--customize-hook=sed -n -e '1i[Service]' -e '1iExecStart=' -e 's,^ExecStart=-/sbin/agetty ,&-a root ,p'"' "$1/lib/systemd/system/serial-getty@.service" > "$1/etc/systemd/system/serial-getty@.service.d/autologin.conf"' \
+ "$@"
+
+# suite target mirror
+set -- "$@" "$SUITE" "$IMAGE" "deb $MIRROR $SUITE main"
+
+set -ex
+
+mmdebstrap "$@"
+
+IMAGESIZE=$(stat -c %s "$IMAGE")
+if test "$IMAGESIZE" -lt "$SIZE"; then
+ truncate -s "$SIZE" "$IMAGE"
+ /sbin/resize2fs "$IMAGE"
+fi
+/sbin/tune2fs -L debvm -i 0 -O extents,uninit_bg,dir_index,has_journal "$IMAGE"
+# Must fsck after tune2fs: https://ext4.wiki.kernel.org/index.php/UpgradeToExt4
+/sbin/fsck.ext4 -fDp "$IMAGE"