From 651ee08f242ffedaa25c37a1a129493d0f2ebe56f2473bfdafa2f13771e05da3 Mon Sep 17 00:00:00 2001 From: Ignaz Forster Date: Fri, 18 Sep 2020 07:12:39 +0000 Subject: [PATCH] Accepting request 835192 from home:favogt:combustion - Overhaul configuration fetching: * Add udev rules to wait for either ignition, combustion or fw_cfg drives * Allow combustion.firstboot in addition to ignition.firstboot M combustion A combustion-prepare.service M combustion.service M module-setup.sh - Bump version to 0.2 OBS-URL: https://build.opensuse.org/request/show/835192 OBS-URL: https://build.opensuse.org/package/show/devel:kubic:ignition/combustion?expand=0&rev=7 --- README | 23 ++++++++++++++--------- combustion | 37 +++++++++++++++++++++++++++++++------ combustion-prepare.service | 9 ++++++--- combustion.changes | 13 +++++++++++++ combustion.rules | 27 +++++++++++++++++++++++++++ combustion.service | 3 ++- combustion.spec | 4 +++- module-setup.sh | 5 +++-- 8 files changed, 99 insertions(+), 22 deletions(-) create mode 100644 combustion.rules diff --git a/README b/README index 61eec9d..56070d8 100644 --- a/README +++ b/README @@ -13,12 +13,17 @@ snapshot, so that no reboot is needed. How to use it ------------- -To be compatible and co-installable with ignition -(https://github.com/coreos/ignition), the configuration files are copied from -a filesystem with the LABEL "ignition". +The configuration files are copied from a filesystem with the LABEL +"combustion", but to be compatible and co-installable with ignition +(https://github.com/coreos/ignition), the LABEL "ignition" is used as fallback. It expects a directory "combustion" at the root level of the filesystem and a file "script" inside, which is executed inside a transactional-update shell. +If a QEMU fw_cfg blob with the name "opt/org.opensuse.combustion/script" is +found, it is preferred and the content of that is used as script. +Example parameter for QEMU: +-fw_cfg name=opt/org.opensuse.combustion/script,file=/var/combustion-script + Example for formatting a USB drive and adding a config, which installs the "vim-small" package and creates a /root/welcome file: @@ -54,12 +59,12 @@ kernel commandline. If this option is found on the kernel cmdline, combustion.service's ConditionKernelCommandLine is fulfilled and it'll be required by initrd.target. -This pulls in combustion-prepare.service, which runs after the config drive -with LABEL=ignition appears. It is mounted and if the "network" flag comment is -present, enables networking for later. After /sysroot is mounted and network is -up (if enabled), combustion.service runs, which tries to activate all -mountpoints in the system's /etc/fstab and then calls transactional-update -in a chroot. +This pulls in combustion-prepare.service, which runs after the config drive or +QEMU fw_cfg blob appears (see combustion.rules for details). It is read and if +the "network" flag comment is present, enables networking for later. +After /sysroot is mounted and network is up (if enabled), combustion.service +runs, which tries to activate all mountpoints in the system's /etc/fstab and +then calls transactional-update in a chroot. In this transactional-update session the script is started and the exit code recorded. If the script failed, transactional-update rollback is called and diff --git a/combustion b/combustion index cde00ae..cecc4f2 100644 --- a/combustion +++ b/combustion @@ -9,14 +9,37 @@ if [ "${1-}" = "--prepare" ]; then # Mount config drive mkdir -p "${config_mount}" - if ! [ -e /dev/disk/by-label/ignition ]; then - echo "No config drive found" - exit 0 + config_drive_found=0 + + # Try fw_cfg first + if [ -e "/sys/firmware/qemu_fw_cfg/by_name/opt/org.opensuse.combustion" ]; then + mkdir -p "${config_mount}/combustion" + if ! cp /sys/firmware/qemu_fw_cfg/by_name/opt/org.opensuse.combustion/script/raw \ + "${config_mount}/combustion/script"; then + echo "Failed to copy script from fw_cfg!" + exit 1 + fi + # TODO: Support other files, e.g. with a tarball or fs image? + + config_drive_found=1 fi - if ! mount -o ro /dev/disk/by-label/ignition "${config_mount}"; then - echo "Failed to mount config drive!" - exit 1 + # Try disks next + for label in combustion ignition; do + [ "${config_drive_found}" = "1" ] && break + [ -e "/dev/disk/by-label/${label}" ] || continue + + if ! mount -o ro /dev/disk/by-label/${label} "${config_mount}"; then + echo "Failed to mount config drive!" + exit 1 + fi + + config_drive_found=1 + done + + if [ "${config_drive_found}" = "0" ]; then + echo "No config drive found" + exit 0 fi # Check for the magic flag "# combustion: network" in the script @@ -44,6 +67,8 @@ cleanup() { if findmnt "${config_mount}" >/dev/null; then umount "${config_mount}" || true rmdir "${config_mount}" || true + else + rm -rf "${config_mount}" || true fi rm -rf "${exchangedir}" || true diff --git a/combustion-prepare.service b/combustion-prepare.service index 81333cb..3a6320e 100644 --- a/combustion-prepare.service +++ b/combustion-prepare.service @@ -2,11 +2,14 @@ Description=Combustion (preparations) DefaultDependencies=false -ConditionKernelCommandLine=ignition.firstboot +# Systemd evaluates Requires/After before conditions, so this unit is pulled in +# even when combustion.service won't run. +ConditionKernelCommandLine=|ignition.firstboot +ConditionKernelCommandLine=|combustion.firstboot # Config drive has to be available -Wants=dev-disk-by\x2dlabel-ignition.device -After=dev-disk-by\x2dlabel-ignition.device +Wants=dev-combustion-config.device +After=dev-combustion-config.device # If both Ignition and Combustion are active make sure to run their # configuration scripts sequentially, as both try to mount the configuration diff --git a/combustion.changes b/combustion.changes index 757bcc4..578d874 100644 --- a/combustion.changes +++ b/combustion.changes @@ -1,3 +1,16 @@ +------------------------------------------------------------------- +Thu Sep 17 10:56:44 UTC 2020 - Fabian Vogt + +- Overhaul configuration fetching: + * Add udev rules to wait for either ignition, combustion or + fw_cfg drives + * Allow combustion.firstboot in addition to ignition.firstboot + M combustion + A combustion-prepare.service + M combustion.service + M module-setup.sh +- Bump version to 0.2 + ------------------------------------------------------------------- Mon Sep 14 09:20:49 UTC 2020 - Ignaz Forster diff --git a/combustion.rules b/combustion.rules new file mode 100644 index 0000000..7ce9c82 --- /dev/null +++ b/combustion.rules @@ -0,0 +1,27 @@ +# SPDX-FileCopyrightText: 2020 SUSE LLC +# SPDX-License-Identifier: GPL-2.0-only + +# These rules are needed to work around two systemd limitations: +# - It's not possible to wait for one of multiple devices to appear +# - ConditionKernelCommandLine is evaluated after Wants/After, +# so it waits for the devices unnecessarily +# Introduce a dev-combustion-config.device unit as alias to the actual device(s). +# This is only used for the .service dependencies. + +# Filesystems with either combustion or ignition as label +ACTION=="add", SUBSYSTEM=="block", ENV{ID_FS_LABEL}=="combustion", ENV{SYSTEMD_ALIAS}+="/dev/combustion/config" +ACTION=="add", SUBSYSTEM=="block", ENV{ID_FS_LABEL}=="ignition", ENV{SYSTEMD_ALIAS}+="/dev/combustion/config" +# QEMU fw_cfg blob with key opt/org.opensuse.combustion +ACTION=="add", SUBSYSTEM=="opt", ENV{DEVPATH}=="/firmware/qemu_fw_cfg/by_name/opt/org.opensuse.combustion", ENV{SYSTEMD_ALIAS}+="/dev/combustion/config", TAG+="systemd" + +# If combustion won't run, alias it to /dev/null to avoid waiting +ACTION=="add", SUBSYSTEM=="mem", ENV{DEVPATH}=="/devices/virtual/mem/null", GOTO="combustion_dev_null" +GOTO="combustion_end" + +LABEL="combustion_dev_null" +# IMPORT has to be on its own as it returns success or not, even with "="... +IMPORT{cmdline}="ignition.firstboot" +IMPORT{cmdline}="combustion.firstboot" +ENV{ignition.firstboot}!="1", ENV{combustion.firstboot}!="1", ENV{SYSTEMD_ALIAS}+="/dev/combustion/config", TAG+="systemd" + +LABEL="combustion_end" diff --git a/combustion.service b/combustion.service index c0f3e9f..129c61a 100644 --- a/combustion.service +++ b/combustion.service @@ -2,7 +2,8 @@ Description=Combustion DefaultDependencies=false -ConditionKernelCommandLine=ignition.firstboot +ConditionKernelCommandLine=|ignition.firstboot +ConditionKernelCommandLine=|combustion.firstboot Requires=sysroot.mount After=sysroot.mount diff --git a/combustion.spec b/combustion.spec index 86120fa..2542248 100644 --- a/combustion.spec +++ b/combustion.spec @@ -17,7 +17,7 @@ Name: combustion -Version: 0.1 +Version: 0.2 Release: 0 Summary: System for initial configuration of appliances License: GPL-2.0-only @@ -29,6 +29,7 @@ Source3: module-setup.sh Source4: combustion.service Source5: combustion-prepare.service Source6: combustion +Source7: combustion.rules Requires: ignition-dracut-grub2 BuildArch: noarch @@ -54,6 +55,7 @@ install -m0644 %{SOURCE3} module-setup.sh install -m0644 %{SOURCE4} combustion.service install -m0644 %{SOURCE5} combustion-prepare.service install -m0755 %{SOURCE6} combustion +install -m0644 %{SOURCE7} combustion.rules %post %{?regenerate_initrd_post} diff --git a/module-setup.sh b/module-setup.sh index 55fbb51..859f794 100644 --- a/module-setup.sh +++ b/module-setup.sh @@ -5,6 +5,7 @@ depends() { install() { inst_simple "${moddir}/combustion.service" "${systemdsystemunitdir}/combustion.service" inst_simple "${moddir}/combustion-prepare.service" "${systemdsystemunitdir}/combustion-prepare.service" + inst_simple "${moddir}/combustion.rules" "/etc/udev/rules.d/70-combustion.rules" mkdir -p "${initdir}/${systemdsystemunitdir}/initrd.target.requires/" ln_r "../combustion.service" "${systemdsystemunitdir}/initrd.target.requires/combustion.service" inst_multiple awk chroot findmnt @@ -13,6 +14,6 @@ install() { # Wait up to 10s (30s on aarch64) for the config drive devtimeout=10 [ "$(uname -m)" = "aarch64" ] && devtimeout=30 - mkdir -p "${initdir}/${systemdsystemunitdir}/dev-disk-by\x2dlabel-ignition.device.d/" - echo -e "[Unit]\nJobTimeoutSec=${devtimeout}" > "${initdir}/${systemdsystemunitdir}/dev-disk-by\x2dlabel-ignition.device.d/timeout.conf" + mkdir -p "${initdir}/${systemdsystemunitdir}/dev-combustion-config.device.d/" + echo -e "[Unit]\nJobTimeoutSec=${devtimeout}" > "${initdir}/${systemdsystemunitdir}/dev-combustion-config.device.d/timeout.conf" }