From: Lennart Poettering Date: Wed, 1 Jul 2026 20:32:45 +0000 (+0200) Subject: ci: add test for the various new sysupdate features X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=b0cf7d60acf24d985092361ac5b0ab6dd95d2351;p=thirdparty%2Fsystemd.git ci: add test for the various new sysupdate features --- diff --git a/test/units/TEST-72-SYSUPDATE.sh b/test/units/TEST-72-SYSUPDATE.sh index 74af9296cc7..84c057c3723 100755 --- a/test/units/TEST-72-SYSUPDATE.sh +++ b/test/units/TEST-72-SYSUPDATE.sh @@ -918,8 +918,9 @@ test -f "$COMPALL/target-b/comp-b-v1.bin" test -d /var/lib/systemd/sysupdate/installdb.comp-a test -d /var/lib/systemd/sysupdate/installdb.comp-b -# --component-all is only supported for the "cleanup" verb, refuse it elsewhere. -(! "$SYSUPDATE" --component-all --verify=no update) +# --component-all is not supported for every verb, so it must be refused where it isn't (e.g. "vacuum"). +# (It *is* supported for update/acquire/cleanup/enable-*/disable-*, which is exercised further down.) +(! "$SYSUPDATE" --component-all --verify=no vacuum) # With the transfer files still in place "cleanup --component-all" is a no-op: # nothing is orphaned. @@ -1449,4 +1450,375 @@ rm -rf "$CONFIGDIR/01-manifest-yes-and-retry.transfer" \ "$SYSUPDATE" components |& grep "No components defined." >/dev/null [[ $(varlinkctl call "$VARLINK_SOCKET" io.systemd.SysUpdate.ListTargets | jq -r '.targets') == "[]" ]] +# ============================================================================ +# Components & features: enable/disable verbs, the --component-{all,suggested} +# and --feature-{all,suggested} switches, plus the Suggest=/SuggestOn…= +# settings. Everything below operates on freshly minted throw-away components +# and features, and cleans up after itself. +# +# Layout used throughout: +# * a component is defined by a transfer directory /run/sysupdate..d/ +# (which is what makes it show up in the 'components' list) plus an optional +# metadata file /run/sysupdate..component carrying Description=/Suggest=/… +# * the enable-component/disable-component verbs write their Enabled= override +# into a drop-in below /etc/sysupdate..component.d/ +# * the enable-feature/disable-feature verbs write their Enabled= override into +# a drop-in below /etc/sysupdate.d/.feature.d/ (default component) or +# /etc/sysupdate..d/.feature.d/ (named component) +# ============================================================================ +CF="$WORKDIR/compfeat" + +# The Suggest…MachineTag= tests below drive the machine tags via /etc/machine-info +# (which the condition logic reads directly); back up any pre-existing file so we +# can restore it afterwards, mirroring TEST-74-AUX-UTILS.machine-tags.sh. +MI_BAK="$WORKDIR/machine-info.orig" +rm -f "$MI_BAK" +[[ -e /etc/machine-info ]] && cp -a /etc/machine-info "$MI_BAK" + +set_machine_tags() { + if [[ -n "${1:-}" ]]; then + echo "TAGS=$1" >/etc/machine-info + else + rm -f /etc/machine-info + fi +} + +restore_machine_info() { + if [[ -e "$MI_BAK" ]]; then + cp -a "$MI_BAK" /etc/machine-info + else + rm -f /etc/machine-info + fi +} + +compfeat_cleanup() { + rm -rf /run/sysupdate.d \ + /run/sysupdate.compx.d /run/sysupdate.compx.component /run/sysupdate.compx.component.d \ + /run/sysupdate.compy.d /run/sysupdate.compy.component /run/sysupdate.compy.component.d \ + /run/sysupdate.compz.d /run/sysupdate.compz.component /run/sysupdate.compz.component.d \ + /etc/sysupdate.d \ + /etc/sysupdate.compx.d /etc/sysupdate.compx.component.d \ + /etc/sysupdate.compy.d /etc/sysupdate.compy.component.d \ + /etc/sysupdate.compz.d /etc/sysupdate.compz.component.d + rm -rf "$CF" +} + +compfeat_reset() { + compfeat_cleanup + mkdir -p "$CF/source" \ + "$CF/target-default" "$CF/target-compx" "$CF/target-compy" "$CF/target-compz" +} + +# (Re)generate the source payloads + SHA256SUMS for a given version. We create a +# payload for every component/feature so a single SHA256SUMS covers them all; +# individual transfers only ever match their own pattern. +compfeat_source() { + local v="${1:?}" + local n + for n in base compx compy compz feata featb featc; do + echo "$n-$v-$RANDOM" >"$CF/source/$n-$v.bin" + done + (cd "$CF/source" && sha256sum -- *.bin >SHA256SUMS) +} + +# Write a regular-file transfer; optional 4th argument gates it behind a feature. +compfeat_transfer() { + local file="${1:?}" pat="${2:?}" tgt="${3:?}" feature="${4:-}" + { + if [[ -n "$feature" ]]; then + printf '[Transfer]\nFeatures=%s\n\n' "$feature" + fi + printf '[Source]\nType=regular-file\nPath=%s\nMatchPattern=%s-@v.bin\n\n' "$CF/source" "$pat" + printf '[Target]\nType=regular-file\nPath=%s\nMatchPattern=%s-@v.bin\nInstancesMax=2\n' "$tgt" "$pat" + } >"$file" +} + +comp_enable_dropin() { echo "/etc/sysupdate.$1.component.d/50-systemd-sysupdate-enabled.conf"; } +feat_enable_dropin_default() { echo "/etc/sysupdate.d/$1.feature.d/50-systemd-sysupdate-enabled.conf"; } +feat_enable_dropin_comp() { echo "/etc/sysupdate.$1.d/$2.feature.d/50-systemd-sysupdate-enabled.conf"; } + +# Assert a generated Enabled= drop-in exists and carries the expected value. +assert_dropin() { + local file="${1:?}" val="${2:?}" + test -f "$file" + grep "^Enabled=$val$" "$file" >/dev/null +} + +# --------------------------------------------------------------------------- +# enable-component / disable-component: explicit selection + observable effect +# --------------------------------------------------------------------------- +compfeat_reset +compfeat_source v1 +mkdir -p /run/sysupdate.compx.d +compfeat_transfer /run/sysupdate.compx.d/01-compx.transfer compx "$CF/target-compx" +# A metadata file carrying a human-readable description (exercises loading of the +# per-component *.component file from the search dirs). +cat >/run/sysupdate.compx.component </dev/null + +# A brand new component (no Enabled= override anywhere) is enabled by default and +# can be updated. +"$SYSUPDATE" --component=compx --verify=no update +test -f "$CF/target-compx/compx-v1.bin" + +# Disable it: this must drop an Enabled=no override next to the definition, and +# subsequent updates for that component must be refused. +"$SYSUPDATE" disable-component compx +assert_dropin "$(comp_enable_dropin compx)" no +(! "$SYSUPDATE" --component=compx --verify=no update) |& grep -F "Component is disabled" >/dev/null + +# Re-enable via the "--component= + no positional argument" form and verify the +# update works again. +"$SYSUPDATE" --component=compx enable-component +assert_dropin "$(comp_enable_dropin compx)" yes +rm -f "$CF/target-compx/compx-v1.bin" +"$SYSUPDATE" --component=compx --verify=no update +test -f "$CF/target-compx/compx-v1.bin" + +# Enabling a component must not conjure a bogus ".component" pseudo-component +# out of the freshly created sysupdate.compx.component.d/ drop-in directory. +(! "$SYSUPDATE" --json=short components | grep -F '"compx.component"' >/dev/null) +"$SYSUPDATE" --json=short components | grep -F '"compx"' >/dev/null + +# --------------------------------------------------------------------------- +# enable-component / disable-component: argument validation & error handling +# --------------------------------------------------------------------------- +# Positional argument and --component= are mutually exclusive. +(! "$SYSUPDATE" --component=compx enable-component compx) |& grep -F "not both" >/dev/null +# Syntactically invalid component name. +(! "$SYSUPDATE" enable-component ../nope) |& grep -F "Component name invalid" >/dev/null +# Unknown component. +(! "$SYSUPDATE" enable-component doesnotexist) |& grep -F "Component not found" >/dev/null +# --definitions= is incompatible with component enablement. +(! "$SYSUPDATE" --definitions="$CF" enable-component compx) |& grep -F "may not be combined" >/dev/null +# The feature-selection switches make no sense here. +(! "$SYSUPDATE" --feature-all enable-component compx) |& grep -F "not supported" >/dev/null + +# --------------------------------------------------------------------------- +# --component-all for enable-component / disable-component +# --------------------------------------------------------------------------- +compfeat_reset +compfeat_source v1 +mkdir -p /run/sysupdate.compx.d /run/sysupdate.compy.d +compfeat_transfer /run/sysupdate.compx.d/01-compx.transfer compx "$CF/target-compx" +compfeat_transfer /run/sysupdate.compy.d/01-compy.transfer compy "$CF/target-compy" + +# Disable *all* components in one go, then re-enable them all. +"$SYSUPDATE" --component-all disable-component +assert_dropin "$(comp_enable_dropin compx)" no +assert_dropin "$(comp_enable_dropin compy)" no +(! "$SYSUPDATE" --component=compx --verify=no update) |& grep -F "Component is disabled" >/dev/null +(! "$SYSUPDATE" --component=compy --verify=no update) |& grep -F "Component is disabled" >/dev/null + +"$SYSUPDATE" --component-all enable-component +assert_dropin "$(comp_enable_dropin compx)" yes +assert_dropin "$(comp_enable_dropin compy)" yes +"$SYSUPDATE" --component=compx --verify=no update +"$SYSUPDATE" --component=compy --verify=no update +test -f "$CF/target-compx/compx-v1.bin" +test -f "$CF/target-compy/compy-v1.bin" + +# --------------------------------------------------------------------------- +# --component-suggested driven by Suggest= (compx suggested, compy not) +# --------------------------------------------------------------------------- +compfeat_reset +compfeat_source v1 +mkdir -p /run/sysupdate.compx.d /run/sysupdate.compy.d +compfeat_transfer /run/sysupdate.compx.d/01-compx.transfer compx "$CF/target-compx" +compfeat_transfer /run/sysupdate.compy.d/01-compy.transfer compy "$CF/target-compy" +cat >/run/sysupdate.compx.component </run/sysupdate.compy.component </dev/null + +# --------------------------------------------------------------------------- +# SuggestOnMachineTag= for a component +# --------------------------------------------------------------------------- +compfeat_reset +compfeat_source v1 +mkdir -p /run/sysupdate.compz.d +compfeat_transfer /run/sysupdate.compz.d/01-compz.transfer compz "$CF/target-compz" +cat >/run/sysupdate.compz.component </run/sysupdate.d/feata.feature </dev/null +"$SYSUPDATE" --verify=no update +test -f "$CF/target-default/base-v1.bin" +test ! -e "$CF/target-default/feata-v1.bin" + +# Enabling the feature must drop an Enabled=yes override and cause the gated +# transfer to be installed on the next update. +"$SYSUPDATE" enable-feature feata +assert_dropin "$(feat_enable_dropin_default feata)" yes +"$SYSUPDATE" --verify=no update +test -f "$CF/target-default/feata-v1.bin" + +# Disabling it again + vacuum must remove the now-orphaned feature resource. +"$SYSUPDATE" disable-feature feata +assert_dropin "$(feat_enable_dropin_default feata)" no +"$SYSUPDATE" --verify=no vacuum +test ! -e "$CF/target-default/feata-v1.bin" + +# Argument validation for the feature verbs. +(! "$SYSUPDATE" enable-feature --feature-all feata) |& grep -F "not both" >/dev/null +(! "$SYSUPDATE" enable-feature 'bad/name') |& grep -F "Feature name invalid" >/dev/null +(! "$SYSUPDATE" --component-suggested enable-feature feata) |& grep -F "not supported" >/dev/null + +# --------------------------------------------------------------------------- +# --feature-all / --feature-suggested (default component) +# --------------------------------------------------------------------------- +compfeat_reset +compfeat_source v1 +mkdir -p /run/sysupdate.d +compfeat_transfer /run/sysupdate.d/01-base.transfer base "$CF/target-default" +# feata: suggested (Suggest=yes); featb: not suggested (Suggest=no); +# featc: suggested on a machine tag we will set below. +cat >/run/sysupdate.d/feata.feature </run/sysupdate.d/featb.feature </run/sysupdate.d/featc.feature </run/sysupdate.d/feata.feature </run/sysupdate.compx.d/featx.feature <