ostree/debian/ostree-boot-examples/modified-deb-ostree-builder

323 lines
9.1 KiB
Bash
Executable File

#!/bin/bash -e
# deb-ostree-builder - Build bootable Debian OSTree commits
# This version has been modified to install extra packages from
# /root/extra-packages.
#
# Copyright (C) 2017 Dan Nicholson <nicholson@endlessm.com>
# Copyright (C) 2019 Simon McVittie
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
PROG=$(readlink -f "$0")
PROGDIR=$(dirname "$PROG")
# Defaults
ARCH=$(dpkg --print-architecture)
BUILDDIR=
GPG_SIGN=()
GPG_HOMEDIR=
usage() {
cat <<EOF
Usage: $0 [OPTION...] CONFIG SUITE REPO
-a, --arch build architecture
-d, --dir build directory
--gpg-sign GPG key ID to use for signing
--gpg-homedir GPG homedir to find keys
-h, --help show this message and exit
deb-ostree-builder constructs a Debian OS for use as a bootable
OSTree. It uses debootstrap to construct the OS, adjusts it to be
compatible with OSTree, and then commits it to a repository.
CONFIG is a multistrap configuration file. SUITE is the distribution
codename. This is used for the OSTree ref and must match the
multistrap configuration. REPO is the path the the OSTree repository
where the commit will be made.
EOF
}
ARGS=$(getopt -n "$0" \
-o a:d:h \
-l arch:,dir:,gpg-sign:,gpg-homedir:,help \
-- "$@")
eval set -- "$ARGS"
while true; do
case "$1" in
-a|--arch)
ARCH=$2
shift 2
;;
-d|--dir)
BUILDDIR=$2
shift 2
;;
--gpg-sign)
GPG_SIGN+=($2)
shift 2
;;
--gpg-homedir)
GPG_HOMEDIR=$2
shift 2
;;
-h|--help)
usage
exit 0
;;
--)
shift
break
;;
esac
done
if [ $# -lt 3 ]; then
echo "Must specify CONFIG, SUITE and REPO" >&2
exit 1
fi
CONFIG=$1
SUITE=$2
REPO=$3
# Mount cleanup handler
DEVICES_MOUNTED=false
cleanup_mounts()
{
if $DEVICES_MOUNTED; then
echo "Unmounting filesystems in $BUILDDIR"
for dir in dev/pts dev sys proc; do
umount "$BUILDDIR/$dir"
done
DEVICES_MOUNTED=false
fi
}
# Exit handler
TMP_BUILDDIR=
cleanup()
{
cleanup_mounts || true
if [ -n "$TMP_BUILDDIR" ]; then
rm -rf "$TMP_BUILDDIR"
fi
}
trap cleanup EXIT
if [ -n "$BUILDDIR" ]; then
# Create specified build directory
echo "Creating $BUILDDIR"
mkdir -p "$BUILDDIR"
else
# Create a temporary build directory in /var/tmp since it could be
# fairly large
TMP_BUILDDIR=$(mktemp -d -p /var/tmp deb-ostree-builder-XXXXXXXX)
BUILDDIR=$TMP_BUILDDIR
echo "Using temporary directory $BUILDDIR for build"
fi
# Ensure that dracut makes generic initramfs instead of looking just
# at the host configuration. This is also in the dracut-config-generic
# package, but that only gets installed after dracut makes the first
# initramfs.
echo "Configuring dracut for generic initramfs"
mkdir -p "$BUILDDIR"/etc/dracut.conf.d
cat > "$BUILDDIR"/etc/dracut.conf.d/90-deb-ostree.conf <<EOF
# Don't make host-specific initramfs
hostonly=no
EOF
# Define a temporary policy-rc.d that ensures that no daemons are
# launched from the installation.
mkdir -p "$BUILDDIR"/usr/sbin
cat > "$BUILDDIR"/usr/sbin/policy-rc.d <<EOF
#!/bin/sh
exit 101
EOF
chmod +x "$BUILDDIR"/usr/sbin/policy-rc.d
# Mount common kernel filesystems. dracut expects /dev to be mounted.
echo "Mounting filesystems in $BUILDDIR"
DEVICES_MOUNTED=true
for dir in proc sys dev dev/pts; do
mkdir -p "$BUILDDIR/$dir"
mount --bind "/$dir" "$BUILDDIR/$dir"
done
# Build with multistrap
echo "Building system with multistrap in $BUILDDIR"
multistrap -f "$CONFIG" -d "$BUILDDIR"
# This is a hack for testing ostree-boot before it is in the Debian archive
if [ -d /root/extra-packages ]; then
mkdir "$BUILDDIR/root/extra-packages"
cp /root/extra-packages/*.deb "$BUILDDIR/root/extra-packages"
install -m644 /etc/resolv.conf "$BUILDDIR/etc/resolv.conf"
chroot "$BUILDDIR" apt -y update
chroot "$BUILDDIR" apt -y install /root/extra-packages/*.deb
rm -fr "$BUILDDIR/root/extra-packages"
fi
# All done with filesystems
cleanup_mounts
# Remove temporary policy-rc.d
rm -f "$BUILDDIR"/usr/sbin/policy-rc.d
# Cleanup cruft
echo "Preparing system for OSTree"
rm -rf \
"$BUILDDIR"/boot/*.bak \
"$BUILDDIR"/etc/apt/sources.list~ \
"$BUILDDIR"/etc/apt/trusted.gpg~ \
"$BUILDDIR"/etc/{passwd,group,shadow,gshadow}- \
"$BUILDDIR"/var/cache/debconf/*-old \
"$BUILDDIR"/var/lib/dpkg/*-old \
"$BUILDDIR"/boot/{initrd.img,vmlinuz} \
"$BUILDDIR"/{initrd.img,vmlinuz}{,.old}
# Remove dbus machine ID cache (makes each system unique)
rm -f "$BUILDDIR"/var/lib/dbus/machine-id "$BUILDDIR"/etc/machine-id
# Remove resolv.conf copied from the host by debootstrap. The settings
# are only valid on the target host and will be populated at runtime.
rm -f "$BUILDDIR"/etc/resolv.conf
# Remove temporary files
rm -rf "$BUILDDIR"/var/cache/man/*
rm -rf "$BUILDDIR"/tmp "$BUILDDIR"/var/tmp
mkdir -p "$BUILDDIR"/tmp "$BUILDDIR"/var/tmp
chmod 1777 "$BUILDDIR"/tmp "$BUILDDIR"/var/tmp
# OSTree uses a single checksum of the combined kernel and initramfs
# to manage boot. Determine the checksum and rename the files the way
# OSTree expects.
echo "Renaming kernel and initramfs per OSTree requirements"
pushd "$BUILDDIR"/boot >/dev/null
vmlinuz_match=(vmlinuz*)
vmlinuz_file=${vmlinuz_match[0]}
initrd_match=(initrd.img* initramfs*)
initrd_file=${initrd_match[0]}
csum=$(cat ${vmlinuz_file} ${initrd_file} | \
sha256sum --binary | \
awk '{print $1}')
echo "OSTree boot checksum: ${csum}"
mv ${vmlinuz_file} ${vmlinuz_file}-${csum}
mv ${initrd_file} ${initrd_file/initrd.img/initramfs}-${csum}
popd >/dev/null
# OSTree only commits files or symlinks
rm -rf "$BUILDDIR"/dev
mkdir -p "$BUILDDIR"/dev
# Fixup home directory base paths for OSTree
sed -i -e 's|DHOME=/home|DHOME=/sysroot/home|g' \
"${BUILDDIR}"/etc/adduser.conf
sed -i -e 's|# HOME=/home|HOME=/sysroot/home|g' \
"${BUILDDIR}"/etc/default/useradd
# Move /etc to /usr/etc.
#
# FIXME: Need to handle passwd and group to be updatable. This can be
# done with libnss-altfiles, though that has other drawbacks.
if [ -d "${BUILDDIR}"/usr/etc ]; then
echo "ERROR: Non-empty /usr/etc found!" >&2
ls -lR "${BUILDDIR}"/usr/etc
exit 1
fi
mv "${BUILDDIR}"/etc "${BUILDDIR}"/usr
# Move dpkg database to /usr so it's accessible after the OS /var is
# mounted, but make a symlink so it works without modifications to dpkg
# or apt
mkdir -p "${BUILDDIR}"/usr/share/dpkg
if [ -e "${BUILDDIR}"/usr/share/dpkg/database ]; then
echo "ERROR: /usr/share/dpkg/database already exists!" >&2
ls -lR "${BUILDDIR}"/usr/share/dpkg/database >&2
exit 1
fi
mv "${BUILDDIR}"/var/lib/dpkg "${BUILDDIR}"/usr/share/dpkg/database
ln -sr "${BUILDDIR}"/usr/share/dpkg/database \
"${BUILDDIR}"/var/lib/dpkg
# tmpfiles.d setup to make the ostree root compatible with persistent
# directories in the sysroot.
cat > "${BUILDDIR}"/usr/lib/tmpfiles.d/ostree.conf <<EOF
d /sysroot/home 0755 root root -
d /sysroot/root 0700 root root -
d /var/opt 0755 root root -
d /var/local 0755 root root -
d /run/media 0755 root root -
L /var/lib/dpkg - - - - ../../usr/share/dpkg/database
EOF
# Create symlinks in the ostree for persistent directories.
mkdir -p "${BUILDDIR}"/sysroot
rm -rf "${BUILDDIR}"/{home,root,media,opt} "${BUILDDIR}"/usr/local
ln -s /sysroot/ostree "${BUILDDIR}"/ostree
ln -s /sysroot/home "${BUILDDIR}"/home
ln -s /sysroot/root "${BUILDDIR}"/root
ln -s /var/opt "${BUILDDIR}"/opt
ln -s /var/local "${BUILDDIR}"/usr/local
ln -s /run/media "${BUILDDIR}"/media
# Now ready to commit. Make the repo if necessary. An archive-z2 repo
# is used since the intention is to use this repo to serve updates
# from.
mkdir -p "$REPO"
if [ ! -f "$REPO"/config ]; then
echo "Initialiazing OSTree repo $REPO"
ostree --repo="$REPO" init --mode=archive-z2
fi
# Make the commit. The ostree ref is flatpak style.
branch="os/debian/$ARCH/$SUITE"
COMMIT_OPTS=(
--repo="$REPO"
--branch="$branch"
--subject="Build $SUITE $ARCH $(date --iso-8601=seconds)"
--skip-if-unchanged
--table-output
)
for id in ${GPG_SIGN[@]}; do
COMMIT_OPTS+=(--gpg-sign="$id")
done
if [ -n "$GPG_HOMEDIR" ]; then
COMMIT_OPTS+=(--gpg-homedir="$GPG_HOMEDIR")
fi
echo "Committing $BUILDDIR to $REPO branch $branch"
ostree commit "${COMMIT_OPTS[@]}" "$BUILDDIR"
# Update the repo summary
SUMMARY_OPTS=(
--repo="$REPO"
--update
)
for id in ${GPG_SIGN[@]}; do
SUMMARY_OPTS+=(--gpg-sign="$id")
done
if [ -n "$GPG_HOMEDIR" ]; then
SUMMARY_OPTS+=(--gpg-homedir="$GPG_HOMEDIR")
fi
echo "Updating $REPO summary file"
ostree summary "${SUMMARY_OPTS[@]}"