diff --git a/dracut/30ignition/coreos-gpt-setup@.service b/dracut/30ignition/coreos-gpt-setup@.service index 30a862f..1034e7a 100644 --- a/dracut/30ignition/coreos-gpt-setup@.service +++ b/dracut/30ignition/coreos-gpt-setup@.service @@ -8,7 +8,7 @@ Requires=%i.device After=%i.device # Run before services that use device nodes, preventing them from racing # with udev activity generated by sgdisk -Before=ignition-setup-base.service ignition-setup-user.service ignition-disks.service +Before=ignition-setup-base.service ignition-setup-user.service ignition-rootfs-save.service ignition-disks.service [Service] Type=oneshot diff --git a/dracut/30ignition/ignition-fetch.service b/dracut/30ignition/ignition-fetch.service index b5c389c..6009911 100644 --- a/dracut/30ignition/ignition-fetch.service +++ b/dracut/30ignition/ignition-fetch.service @@ -3,7 +3,6 @@ Description=Ignition (fetch) DefaultDependencies=false Before=ignition-complete.target -After=basic.target # Run after ignition-setup has run because ignition-setup # may copy in new/different ignition configs for us to consume. diff --git a/dracut/31ignition-ostree/ignition-dracut-rootfs-manual.sh b/dracut/31ignition-ostree/ignition-dracut-rootfs-manual.sh new file mode 100755 index 0000000..144549d --- /dev/null +++ b/dracut/31ignition-ostree/ignition-dracut-rootfs-manual.sh @@ -0,0 +1,41 @@ +#!/bin/bash +set -euo pipefail + +rootmnt=/mnt/rootfs +mkdir -p $rootmnt +mount /dev/disk/by-label/root $rootmnt +igntmp=/run/ignition-rootfs + +case "${1:-}" in + save) + for toplevel in boot ostree; do + mkdir -p ${igntmp}/${toplevel} + chcon -h --reference=${rootmnt}/${toplevel} ${igntmp}/${toplevel} + done + # We copy the repo, /var and the .origin file; if we tried to copy + # everything with e.g. `cp` we'd end up breaking hardlinks. + repo=${igntmp}/ostree/repo + ostree --repo=${repo} init --mode=bare + cp ${rootmnt}/ostree/repo/config ${repo}/config + ostree --repo=${repo} pull-local ${rootmnt}/ostree/repo + stateroot=$(ls ${rootmnt}/ostree/deploy) + statepath=ostree/deploy/${stateroot} + mkdir -p ${igntmp}/${statepath} + cp -a ${rootmnt}/${statepath}/var ${igntmp}/${statepath}/var + mkdir -p ${igntmp}/${statepath}/deploy + cp -a ${rootmnt}/${statepath}/deploy/*.origin ${igntmp}/${statepath}/deploy/ + commit=$(cd ${stateroot} && ls *.0 | cut -f 1 -d '.') + echo "${commit}" > ${igntmp}/ostree-commit + ;; + restore) + echo "Restoring ostree repo..." + mv -T ${igntmp}/ostree ${rootmnt}/ostree + echo "...done" + commit=$(cat ${igntmp}/ostree-commit) + echo "Redeploying OSTree commit ${commit} ..." + ostree admin --sysroot=${rootmnt} deploy ${commit} + ;; + *) + echo "Unsupported operation: ${1:-}" + ;; +esac diff --git a/dracut/31ignition-ostree/ignition-dracut-rootfs.sh b/dracut/31ignition-ostree/ignition-dracut-rootfs.sh new file mode 100755 index 0000000..6837ed6 --- /dev/null +++ b/dracut/31ignition-ostree/ignition-dracut-rootfs.sh @@ -0,0 +1,49 @@ +#!/bin/bash +set -euo pipefail + +rootmnt=/sysroot +tmproot=/run/ignition-rootfs + +case "${1:-}" in + detect) + # This is obviously crude; perhaps in the future we could change ignition's `fetch` + # stage to write out a file if the rootfs is being replaced or so. But eh, it + # works for now. + has_rootfs=$(jq '.storage?.filesystems? // [] | map(select(.label == "root")) | length' < /run/ignition.json) + if [ "${has_rootfs}" = "0" ]; then + exit 0 + fi + echo "Detected rootfs replacement in fetched Ignition config: /run/ignition.json" + mkdir "${tmproot}" + ;; + save) + # This one is in a private mount namespace since we're not "offically" mounting + mount /dev/disk/by-label/root $rootmnt + echo "Moving rootfs to RAM..." + # OSTree added the immutable bit on the deployment root, and + # cosa's create_disk added it to the rootfs + chattr -i ${rootmnt} ${rootmnt}/ostree/deploy/*/deploy/*.0 + for x in boot ostree; do + # TODO; copy instead of mv to avoid writes, since we're just + # about to blow away the whole FS anyways? + mv -Tn ${rootmnt}/${x} ${tmproot}/${x} + done + umount ${rootmnt} + echo "Moved rootfs to RAM, pending redeployment: ${tmproot}" + ;; + restore) + # This one is in a private mount namespace since we're not "offically" mounting + mount /dev/disk/by-label/root $rootmnt + echo "Restoring rootfs from RAM..." + for x in boot ostree; do + mv -Tn ${tmproot}/${x} ${rootmnt}/${x} + done + # And restore the immutable bits + chattr +i ${rootmnt}/ostree/deploy/*/deploy/*.0 ${rootmnt} + echo "...done" + umount $rootmnt + ;; + *) + echo "Unsupported operation: ${1:-}" + ;; +esac diff --git a/dracut/31ignition-ostree/ignition-rootfs-detect.service b/dracut/31ignition-ostree/ignition-rootfs-detect.service new file mode 100644 index 0000000..da093de --- /dev/null +++ b/dracut/31ignition-ostree/ignition-rootfs-detect.service @@ -0,0 +1,18 @@ +[Unit] +Description=Ignition OSTree: detect rootfs replacement +DefaultDependencies=false +After=ignition-fetch.service +Before=ignition-disks.service +Before=initrd-root-fs.target +Before=sysroot.mount +ConditionKernelCommandLine=ostree + +# This stage requires udevd to detect disks +Requires=systemd-udevd.service +After=systemd-udevd.service + +[Service] +Type=oneshot +RemainAfterExit=yes +EnvironmentFile=/run/ignition.env +ExecStart=/usr/libexec/ignition-dracut-rootfs detect \ No newline at end of file diff --git a/dracut/31ignition-ostree/ignition-rootfs-restore.service b/dracut/31ignition-ostree/ignition-rootfs-restore.service new file mode 100644 index 0000000..462b0cb --- /dev/null +++ b/dracut/31ignition-ostree/ignition-rootfs-restore.service @@ -0,0 +1,16 @@ +[Unit] +Description=Ignition OSTree: restore rootfs +DefaultDependencies=false +After=ignition-disks.service +Before=sysroot.mount + +ConditionKernelCommandLine=ostree +ConditionPathIsDirectory=/run/ignition-rootfs + +[Service] +Type=oneshot +RemainAfterExit=yes +EnvironmentFile=/run/ignition.env +# So we can transiently mount sysroot +MountFlags=slave +ExecStart=/usr/libexec/ignition-dracut-rootfs restore \ No newline at end of file diff --git a/dracut/31ignition-ostree/ignition-rootfs-save.service b/dracut/31ignition-ostree/ignition-rootfs-save.service new file mode 100644 index 0000000..57f0d64 --- /dev/null +++ b/dracut/31ignition-ostree/ignition-rootfs-save.service @@ -0,0 +1,15 @@ +[Unit] +Description=Ignition OSTree: save rootfs +DefaultDependencies=false +After=ignition-rootfs-detect.service +Before=ignition-disks.service +ConditionKernelCommandLine=ostree +ConditionPathIsDirectory=/run/ignition-rootfs + +[Service] +Type=oneshot +RemainAfterExit=yes +EnvironmentFile=/run/ignition.env +# So we can transiently mount sysroot +MountFlags=slave +ExecStart=/usr/libexec/ignition-dracut-rootfs save \ No newline at end of file diff --git a/dracut/31ignition-ostree/module-setup.sh b/dracut/31ignition-ostree/module-setup.sh new file mode 100755 index 0000000..517ddd5 --- /dev/null +++ b/dracut/31ignition-ostree/module-setup.sh @@ -0,0 +1,27 @@ +#!/bin/bash +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh + +depends() { + echo ignition ostree +} + +install_ignition_unit() { + local unit="$1"; shift + local target="${1:-ignition-complete.target}"; shift + local instantiated="${1:-$unit}"; shift + inst_simple "$moddir/$unit" "$systemdsystemunitdir/$unit" + mkdir -p "$initdir/$systemdsystemunitdir/$target.requires" + ln_r "../$unit" "$systemdsystemunitdir/$target.requires/$instantiated" +} + +install() { + inst_multiple ostree tar chattr jq + + inst_script "$moddir/ignition-dracut-rootfs.sh" \ + "/usr/libexec/ignition-dracut-rootfs" + + install_ignition_unit ignition-rootfs-detect.service + install_ignition_unit ignition-rootfs-save.service + install_ignition_unit ignition-rootfs-restore.service +}