Fix cups-browsed removal migration
This commit is contained in:
+39
-116
@@ -1,149 +1,72 @@
|
|||||||
echo "Temporarily remove automatic printer discovery"
|
echo "Temporarily remove automatic printer discovery"
|
||||||
|
|
||||||
# cups-browsed is the daemon that watches the network and creates print queues
|
|
||||||
# by itself. Hardening it (1787815267) took a root daemon with a predictable
|
|
||||||
# cache down to a confined service account, but a daemon that turns anything
|
|
||||||
# advertising itself on the network into a print queue is a lot of exposure for
|
|
||||||
# a convenience, so it comes out of the default install while that is reworked.
|
|
||||||
# Temporarily, and only the discovery half: CUPS itself stays, printing keeps
|
|
||||||
# working, and a printer is added by hand in Print Settings instead of
|
|
||||||
# appearing on its own.
|
|
||||||
machine_marker="${OMARCHY_CUPS_BROWSED_REMOVAL_MARKER:-/var/lib/omarchy/migrations/1788009111}"
|
machine_marker="${OMARCHY_CUPS_BROWSED_REMOVAL_MARKER:-/var/lib/omarchy/migrations/1788009111}"
|
||||||
|
|
||||||
[[ ! -e $machine_marker ]] || exit 0
|
[[ ! -e $machine_marker ]] || exit 0
|
||||||
|
|
||||||
# Nothing to do on a machine that never had it. Checked before any sudo so those
|
|
||||||
# runs never prompt for a password, and before the marker so no machine pays a
|
|
||||||
# password prompt for a removal it does not need.
|
|
||||||
omarchy-pkg-present cups-browsed || exit 0
|
omarchy-pkg-present cups-browsed || exit 0
|
||||||
|
|
||||||
# Ask pacman whether the removal is possible before touching the service. If
|
# Check the full removal transaction before changing the service or queues.
|
||||||
# something here depends on cups-browsed, the alternative is a machine whose
|
pacman -Rs --print cups-browsed >/dev/null
|
||||||
# discovery daemon has been stopped and whose package removal then failed --
|
|
||||||
# broken rather than removed.
|
|
||||||
if ! pacman -R --print cups-browsed >/dev/null 2>&1; then
|
|
||||||
echo " Something else on this machine still depends on cups-browsed, so it is staying installed."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Disable before removing, and this is the only window in which it works.
|
# Disable the unit while its package still owns the unit file so systemd can
|
||||||
# pacman deletes the unit file but not the enable symlink systemd wrote under
|
# remove the enable symlink cleanly.
|
||||||
# /etc, and once the unit is gone `systemctl disable` refuses it by name and
|
|
||||||
# leaves the symlink dangling with nothing left that can clean it. Stopping is
|
|
||||||
# part of the same step: a daemon whose executable has been unlinked keeps
|
|
||||||
# running until it is told not to. A masked or already-disabled unit reports
|
|
||||||
# not-enabled and is left alone. Doing it before the queue list is read also
|
|
||||||
# means the list cannot grow a new entry while it is being acted on.
|
|
||||||
if systemctl is-enabled --quiet cups-browsed.service 2>/dev/null; then
|
if systemctl is-enabled --quiet cups-browsed.service 2>/dev/null; then
|
||||||
sudo systemctl disable --now cups-browsed.service
|
sudo systemctl disable --now cups-browsed.service >/dev/null
|
||||||
elif systemctl is-active --quiet cups-browsed.service 2>/dev/null; then
|
elif systemctl is-active --quiet cups-browsed.service 2>/dev/null; then
|
||||||
sudo systemctl stop cups-browsed.service
|
sudo systemctl stop cups-browsed.service >/dev/null
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# cups-browsed keeps the queues it generated when it stops --
|
# cups-browsed leaves its implicitclass queues behind when stopped. Remove idle
|
||||||
# KeepGeneratedQueuesOnShutdown defaults to Yes and nothing here overrides it --
|
# discovery queues before removing the backend they require, but leave queues
|
||||||
# and those queues route through its own implicitclass backend, which goes with
|
# with jobs for the user to resolve.
|
||||||
# the package, so none of them can print again. The idle ones are removed rather
|
|
||||||
# than left in Print Settings looking like printers; the ones with jobs on them
|
|
||||||
# are handled below. Only queues on that backend: a printer added by hand has an
|
|
||||||
# ipp:// or usb:// device and is left alone.
|
|
||||||
#
|
#
|
||||||
# LC_ALL=C because lpstat translates "device for", and on a German or French
|
# A healthy CUPS server with no configured printers reports this condition on
|
||||||
# machine the untranslated pattern would match nothing and read exactly like a
|
# stderr and exits 1. Treat that as an empty queue list; every other failure
|
||||||
# machine that had no queues to clean up. Captured rather than piped so that
|
# keeps the migration pending so it can be retried.
|
||||||
# failing to reach cupsd is distinguishable from finding nothing. The name is
|
if queue_report=$(LC_ALL=C lpstat -v 2>&1); then
|
||||||
# matched greedily because CUPS allows a colon in a queue name but never a
|
:
|
||||||
# space, so the last ": implicitclass://" is the separator and an earlier colon
|
elif [[ $queue_report == "lpstat: No destinations added." ]]; then
|
||||||
# belongs to the name.
|
queue_report=""
|
||||||
if ! queue_report=$(LC_ALL=C lpstat -v 2>/dev/null); then
|
else
|
||||||
echo " Could not ask CUPS which queues discovery had created."
|
printf '%s\n' "$queue_report" >&2
|
||||||
echo " Discovery is off, but cups-browsed stays installed; remove it by hand once CUPS answers."
|
exit 1
|
||||||
exit 0
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
generated_queues=$(printf '%s\n' "$queue_report" |
|
generated_queues=$(printf '%s\n' "$queue_report" |
|
||||||
sed -n 's|^device for \(.*\): implicitclass://.*|\1|p')
|
sed -n 's|^device for \(.*\): implicitclass://.*|\1|p')
|
||||||
|
|
||||||
unremoved=0
|
|
||||||
|
|
||||||
while IFS= read -r queue; do
|
while IFS= read -r queue; do
|
||||||
[[ -n $queue ]] || continue
|
[[ -n $queue ]] || continue
|
||||||
|
|
||||||
# A queue name is whatever the printer advertised, put through cups-browsed's
|
if ! reject_error=$(sudo cupsreject -r "Printer discovery has been removed from Omarchy" "$queue" 2>&1); then
|
||||||
# own sanitizer. `lpstat -o` takes its destination as an optional argument, so
|
if LC_ALL=C lpstat -p "$queue" >/dev/null 2>&1; then
|
||||||
# a leading dash reads as the next option and a comma as a list separator, and
|
printf '%s\n' "$reject_error" >&2
|
||||||
# "all" is its word for every destination. The jobs on such a queue cannot be
|
exit 1
|
||||||
# asked about, so it is left alone and named. Never a reason to keep the
|
else
|
||||||
# package, though: that would hand a printer that picked its own name a veto
|
|
||||||
# over the removal.
|
|
||||||
if [[ $queue == -* || $queue == *,* || $queue == "all" ]]; then
|
|
||||||
echo " Cannot safely ask about jobs on the queue named '$queue'; remove it in Print Settings."
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Close the queue to new jobs before looking at what is on it. Otherwise a job
|
|
||||||
# submitted between the check and the removal -- the sudo below can sit at a
|
|
||||||
# password prompt for as long as someone takes to type it -- is cancelled by a
|
|
||||||
# deletion that decided the queue was empty. It also stops more jobs piling
|
|
||||||
# onto a queue that is being left behind and can no longer route them.
|
|
||||||
if ! sudo cupsreject -r "Printer discovery has been removed from Omarchy" "$queue"; then
|
|
||||||
echo " Could not stop $queue accepting new jobs, so it is being left alone."
|
|
||||||
unremoved=1
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Removing a queue cancels the jobs on it. implicitclass needs cups-browsed
|
|
||||||
# only to pick a destination, so a job already past that point finishes on its
|
|
||||||
# own; one still waiting cannot, because the daemon that would route it has
|
|
||||||
# stopped. Neither is this migration's to throw away, so the queue is left for
|
|
||||||
# whoever owns them, and what will and will not happen is said rather than
|
|
||||||
# implied.
|
|
||||||
if job_report=$(LC_ALL=C lpstat -o "$queue" 2>/dev/null); then
|
|
||||||
if [[ -n $job_report ]]; then
|
|
||||||
echo " $queue still has jobs and is no longer taking new ones, so it is being left alone."
|
|
||||||
echo " Anything already sent to the printer finishes; anything still waiting cannot be routed now."
|
|
||||||
echo " Cancel what is left and remove the queue in Print Settings."
|
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if job_report=$(LC_ALL=C lpstat -o "$queue" 2>&1); then
|
||||||
|
[[ -z $job_report ]] || continue
|
||||||
|
elif LC_ALL=C lpstat -p "$queue" >/dev/null 2>&1; then
|
||||||
|
printf '%s\n' "$job_report" >&2
|
||||||
|
exit 1
|
||||||
else
|
else
|
||||||
echo " Could not check for jobs on $queue, so it is being left alone."
|
# The queue disappeared after the initial snapshot, which is already the
|
||||||
|
# desired state.
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# A queue another administrator removed while this was running is a queue that
|
if ! delete_error=$(sudo lpadmin -x "$queue" 2>&1); then
|
||||||
# is gone, which is the outcome wanted -- not a failure worth keeping the
|
# Treat a concurrent disappearance as success. A queue that still exists
|
||||||
# package for.
|
# means CUPS did not complete the deletion, so retry the migration later.
|
||||||
if ! sudo lpadmin -x "$queue"; then
|
|
||||||
if LC_ALL=C lpstat -p "$queue" >/dev/null 2>&1; then
|
if LC_ALL=C lpstat -p "$queue" >/dev/null 2>&1; then
|
||||||
echo " Could not remove the queue $queue."
|
printf '%s\n' "$delete_error" >&2
|
||||||
unremoved=1
|
exit 1
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
done <<<"$generated_queues"
|
done <<<"$generated_queues"
|
||||||
|
|
||||||
# A queue that would not delete is a CUPS that is not answering as expected, so
|
omarchy-pkg-drop cups-browsed >/dev/null
|
||||||
# the package stays rather than deleting the backend out from under it. Discovery
|
|
||||||
# is stopped either way, which is the half that mattered. omarchy-migrate records
|
|
||||||
# this migration for the user as soon as it exits zero, so this is where the
|
|
||||||
# machine stays until someone removes the package by hand -- said plainly rather
|
|
||||||
# than dressed up as a retry.
|
|
||||||
if ((unremoved)); then
|
|
||||||
echo " Leaving cups-browsed installed. Discovery is off; remove the package by hand once those queues are gone."
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Raw pacman rather than omarchy-pkg-drop, which passes -n: that discards the
|
|
||||||
# files pacman has marked as backups instead of renaming them .pacsave, and
|
|
||||||
# /etc/cups/cups-browsed.conf is one of them. A removal meant to be temporary
|
|
||||||
# should leave the machine's copy of its own configuration behind. Plain -R
|
|
||||||
# rather than -Rs, so this only ever removes the one package it names: -s also
|
|
||||||
# sweeps dependencies that have become unneeded, which is nothing today but is
|
|
||||||
# a promise the dependency graph of a rolling distribution cannot keep.
|
|
||||||
# pacman's systemd hook reloads the system manager once the unit file goes.
|
|
||||||
sudo pacman -R --noconfirm cups-browsed
|
|
||||||
|
|
||||||
# Migration state is per user, so every account on this machine runs every
|
|
||||||
# migration. Without machine-wide state, an account whose first run comes after
|
|
||||||
# someone deliberately reinstalled cups-browsed would quietly take it back out
|
|
||||||
# again. This records that the machine has had its one removal.
|
|
||||||
sudo install -Dm644 /dev/null "$machine_marker"
|
sudo install -Dm644 /dev/null "$machine_marker"
|
||||||
|
|||||||
@@ -83,6 +83,7 @@ verify_printing_security() {
|
|||||||
|
|
||||||
for path in \
|
for path in \
|
||||||
/etc/cups/cups-browsed.conf \
|
/etc/cups/cups-browsed.conf \
|
||||||
|
/etc/cups/cups-browsed.conf.pacsave \
|
||||||
/usr/bin/cups-browsed \
|
/usr/bin/cups-browsed \
|
||||||
/usr/lib/cups/backend/implicitclass \
|
/usr/lib/cups/backend/implicitclass \
|
||||||
/usr/lib/systemd/system/cups-browsed.service \
|
/usr/lib/systemd/system/cups-browsed.service \
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
source "$(cd -- "$(dirname -- "$0")" && pwd)/base-test.sh"
|
||||||
|
|
||||||
migration="$ROOT/migrations/1788009111.sh"
|
migration="$ROOT/migrations/1788009111.sh"
|
||||||
|
|
||||||
@@ -12,26 +12,31 @@ trap 'rm -rf "$test_tmp"' EXIT
|
|||||||
mock_bin="$test_tmp/bin"
|
mock_bin="$test_tmp/bin"
|
||||||
mkdir -p "$mock_bin" "$test_tmp/var/lib/omarchy/migrations"
|
mkdir -p "$mock_bin" "$test_tmp/var/lib/omarchy/migrations"
|
||||||
|
|
||||||
# Every privileged step is stubbed: a real run here would take printer discovery
|
# Every privileged step is stubbed: a real run here would take printer
|
||||||
# off the developer's own machine.
|
# discovery off the developer's own machine.
|
||||||
cat >"$mock_bin/omarchy-pkg-present" <<'SH'
|
cat >"$mock_bin/omarchy-pkg-present" <<'SH'
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
[[ " $BROWSED_INSTALLED " == *" $1 "* ]]
|
[[ " $BROWSED_INSTALLED " == *" $1 "* ]]
|
||||||
SH
|
SH
|
||||||
|
|
||||||
# pacman answers the removal preflight from the state each case sets up, and
|
cat >"$mock_bin/omarchy-pkg-drop" <<'SH'
|
||||||
# logs the removal itself so the flags it is called with are visible.
|
#!/bin/bash
|
||||||
|
printf 'omarchy-pkg-drop\t%s\n' "$*" >>"$BROWSED_LOG"
|
||||||
|
[[ -z $BROWSED_DROP_FAILS ]]
|
||||||
|
SH
|
||||||
|
|
||||||
cat >"$mock_bin/pacman" <<'SH'
|
cat >"$mock_bin/pacman" <<'SH'
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
printf 'pacman\t%s\n' "$*" >>"$BROWSED_LOG"
|
printf 'pacman\t%s\n' "$*" >>"$BROWSED_LOG"
|
||||||
[[ $* == *--print* ]] || exit 0
|
if [[ $* == *--print* ]]; then
|
||||||
[[ -z $BROWSED_REMOVAL_BLOCKED ]]
|
[[ -z $BROWSED_REMOVAL_BLOCKED ]]
|
||||||
|
fi
|
||||||
SH
|
SH
|
||||||
|
|
||||||
cat >"$mock_bin/systemctl" <<'SH'
|
cat >"$mock_bin/systemctl" <<'SH'
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
printf 'systemctl\t%s\n' "$*" >>"$BROWSED_LOG"
|
printf 'systemctl\t%s\n' "$*" >>"$BROWSED_LOG"
|
||||||
unit=${*: -1}
|
unit=$3
|
||||||
case $1 in
|
case $1 in
|
||||||
is-enabled) [[ " $BROWSED_ENABLED " == *" $unit "* ]] ;;
|
is-enabled) [[ " $BROWSED_ENABLED " == *" $unit "* ]] ;;
|
||||||
is-active) [[ " $BROWSED_ACTIVE " == *" $unit "* ]] ;;
|
is-active) [[ " $BROWSED_ACTIVE " == *" $unit "* ]] ;;
|
||||||
@@ -51,386 +56,303 @@ printf 'install\t%s\n' "$*" >>"$BROWSED_LOG"
|
|||||||
exec /usr/bin/install "$@"
|
exec /usr/bin/install "$@"
|
||||||
SH
|
SH
|
||||||
|
|
||||||
# lpstat reports the queues cups-browsed generated. Only those route through its
|
# lpstat reports only package-generated queues on the implicitclass backend.
|
||||||
# implicitclass backend; the ipp:// and usb:// entries are printers a person
|
# Manual ipp:// and usb:// printers must survive.
|
||||||
# added and must survive.
|
|
||||||
cat >"$mock_bin/lpstat" <<'SH'
|
cat >"$mock_bin/lpstat" <<'SH'
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
printf 'lpstat\t%s\n' "$*" >>"$BROWSED_LOG"
|
printf 'lpstat\t%s\n' "$*" >>"$BROWSED_LOG"
|
||||||
case $1 in
|
case $1 in
|
||||||
-v)
|
-v)
|
||||||
[[ -z $BROWSED_LPSTAT_FAILS ]] || exit 1
|
if [[ -n $BROWSED_LPSTAT_FAILS ]]; then
|
||||||
# lpstat translates "device for". Only a caller that pinned the locale gets
|
echo "lpstat: Scheduler is not responding." >&2
|
||||||
# the string the migration parses.
|
exit 1
|
||||||
if [[ ${LC_ALL:-} == "C" ]]; then
|
elif [[ -z $BROWSED_QUEUES ]]; then
|
||||||
|
echo "lpstat: No destinations added." >&2
|
||||||
|
exit 1
|
||||||
|
elif [[ $LC_ALL == "C" ]]; then
|
||||||
printf '%s\n' "$BROWSED_QUEUES"
|
printf '%s\n' "$BROWSED_QUEUES"
|
||||||
else
|
else
|
||||||
printf '%s\n' "$BROWSED_QUEUES" | sed 's|^device for |Gerät für |'
|
printf '%s\n' "$BROWSED_QUEUES" | sed 's|^device for |Gerät für |'
|
||||||
fi
|
fi
|
||||||
;;
|
;;
|
||||||
-p)
|
-p)
|
||||||
# A destination that no longer exists is unknown to lpstat.
|
|
||||||
[[ " $BROWSED_GONE_QUEUES " != *" $2 "* ]]
|
[[ " $BROWSED_GONE_QUEUES " != *" $2 "* ]]
|
||||||
exit $?
|
|
||||||
;;
|
;;
|
||||||
-o)
|
-o)
|
||||||
[[ -z $BROWSED_LPSTAT_O_FAILS ]] || exit 1
|
if [[ -n $BROWSED_LPSTAT_O_FAILS ]]; then
|
||||||
# Jobs are listed per queue: "<queue>-<id> <user> <size>".
|
echo "lpstat: Scheduler is not responding." >&2
|
||||||
[[ " $BROWSED_BUSY_QUEUES " == *" $2 "* ]] && printf '%s-7 alice 1024\n' "$2"
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ " $BROWSED_BUSY_QUEUES " == *" $2 "* ]]; then
|
||||||
|
printf '%s-7 alice 1024\n' "$2"
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
exit 0
|
|
||||||
SH
|
SH
|
||||||
|
|
||||||
cat >"$mock_bin/lpadmin" <<'SH'
|
cat >"$mock_bin/lpadmin" <<'SH'
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
printf 'lpadmin\t%s\n' "$*" >>"$BROWSED_LOG"
|
printf 'lpadmin\t%s\n' "$*" >>"$BROWSED_LOG"
|
||||||
[[ -z $BROWSED_LPADMIN_FAILS ]]
|
if [[ -n $BROWSED_LPADMIN_FAILS ]]; then
|
||||||
|
echo "lpadmin: Printer does not exist." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
SH
|
SH
|
||||||
|
|
||||||
cat >"$mock_bin/cupsreject" <<'SH'
|
cat >"$mock_bin/cupsreject" <<'SH'
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
printf 'cupsreject\t%s\n' "$*" >>"$BROWSED_LOG"
|
printf 'cupsreject\t%s\n' "$*" >>"$BROWSED_LOG"
|
||||||
[[ -z $BROWSED_REJECT_FAILS ]]
|
if [[ -n $BROWSED_REJECT_FAILS ]]; then
|
||||||
|
echo "cupsreject: Unable to reject jobs." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
SH
|
SH
|
||||||
|
|
||||||
chmod +x "$mock_bin"/*
|
chmod +x "$mock_bin"/*
|
||||||
|
|
||||||
log="$test_tmp/actions.log"
|
log="$test_tmp/actions.log"
|
||||||
output="$test_tmp/migration.out"
|
output="$test_tmp/migration.out"
|
||||||
marker="$test_tmp/var/lib/omarchy/migrations/1788009111"
|
errors="$test_tmp/migration.err"
|
||||||
|
status=0
|
||||||
|
|
||||||
|
start_case() {
|
||||||
|
installed="cups-browsed"
|
||||||
|
enabled="cups-browsed.service"
|
||||||
|
active="cups-browsed.service cups.service"
|
||||||
|
blocked=""
|
||||||
|
drop_fails=""
|
||||||
|
queues=""
|
||||||
|
busy=""
|
||||||
|
lpstat_fails=""
|
||||||
|
lpstat_o_fails=""
|
||||||
|
lpadmin_fails=""
|
||||||
|
reject_fails=""
|
||||||
|
gone_queues=""
|
||||||
|
use_marker="$test_tmp/var/lib/omarchy/migrations/$1"
|
||||||
|
rm -f "$use_marker"
|
||||||
|
}
|
||||||
|
|
||||||
run_migration() {
|
run_migration() {
|
||||||
: >"$log"
|
: >"$log"
|
||||||
: >"$output"
|
: >"$output"
|
||||||
|
: >"$errors"
|
||||||
|
|
||||||
# No LC_ALL in the environment, so the mock above sees "C" only when the
|
if env -u LC_ALL -u LANGUAGE \
|
||||||
# migration pinned it for the call itself -- which is what a non-English
|
|
||||||
# desktop depends on.
|
|
||||||
env -u LC_ALL -u LANGUAGE \
|
|
||||||
BROWSED_LOG="$log" \
|
BROWSED_LOG="$log" \
|
||||||
BROWSED_INSTALLED="${installed:-}" \
|
BROWSED_INSTALLED="$installed" \
|
||||||
BROWSED_ENABLED="${enabled:-}" \
|
BROWSED_ENABLED="$enabled" \
|
||||||
BROWSED_ACTIVE="${active:-}" \
|
BROWSED_ACTIVE="$active" \
|
||||||
BROWSED_REMOVAL_BLOCKED="${blocked:-}" \
|
BROWSED_REMOVAL_BLOCKED="$blocked" \
|
||||||
BROWSED_QUEUES="${queues:-}" \
|
BROWSED_DROP_FAILS="$drop_fails" \
|
||||||
BROWSED_BUSY_QUEUES="${busy:-}" \
|
BROWSED_QUEUES="$queues" \
|
||||||
BROWSED_LPSTAT_FAILS="${lpstat_fails:-}" \
|
BROWSED_BUSY_QUEUES="$busy" \
|
||||||
BROWSED_LPADMIN_FAILS="${lpadmin_fails:-}" \
|
BROWSED_LPSTAT_FAILS="$lpstat_fails" \
|
||||||
BROWSED_LPSTAT_O_FAILS="${lpstat_o_fails:-}" \
|
BROWSED_LPSTAT_O_FAILS="$lpstat_o_fails" \
|
||||||
BROWSED_REJECT_FAILS="${reject_fails:-}" \
|
BROWSED_LPADMIN_FAILS="$lpadmin_fails" \
|
||||||
BROWSED_GONE_QUEUES="${gone_queues:-}" \
|
BROWSED_REJECT_FAILS="$reject_fails" \
|
||||||
|
BROWSED_GONE_QUEUES="$gone_queues" \
|
||||||
PATH="$mock_bin:$PATH" \
|
PATH="$mock_bin:$PATH" \
|
||||||
OMARCHY_PATH="$ROOT" \
|
OMARCHY_PATH="$ROOT" \
|
||||||
OMARCHY_CUPS_BROWSED_REMOVAL_MARKER="${use_marker:-$marker}" \
|
OMARCHY_CUPS_BROWSED_REMOVAL_MARKER="$use_marker" \
|
||||||
bash -euo pipefail "$migration" >"$output"
|
bash -euo pipefail "$migration" >"$output" 2>"$errors"; then
|
||||||
|
status=0
|
||||||
|
else
|
||||||
|
status=$?
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# ------------------------------------------------ a machine that has discovery
|
assert_description_only() {
|
||||||
|
[[ $(<"$output") == "Temporarily remove automatic printer discovery" ]] ||
|
||||||
|
fail "the migration prints only its description" "$(cat "$output")"
|
||||||
|
}
|
||||||
|
|
||||||
installed="cups-browsed"
|
assert_quiet_success() {
|
||||||
enabled="cups-browsed.service"
|
(( status == 0 )) || fail "the migration succeeds" "$(cat "$errors")"
|
||||||
active="cups-browsed.service cups.service"
|
assert_description_only
|
||||||
blocked=""
|
[[ ! -s $errors ]] || fail "a successful migration is quiet" "$(cat "$errors")"
|
||||||
# CUPS allows a colon in a destination name but never a space, so "Front:Desk"
|
}
|
||||||
# is a legal queue and the separator is the last ": implicitclass://".
|
|
||||||
queues=$'device for Office: implicitclass://Office/\ndevice for Front:Desk: implicitclass://Front:Desk/\ndevice for -p: implicitclass://-p/\ndevice for a,b: implicitclass://a,b/\ndevice for all: implicitclass://all/\ndevice for Desk: ipp://192.168.1.9/ipp/print\ndevice for Attic: usb://HP/LaserJet%20P1102'
|
# ------------------------------------------------ a machine that has printers
|
||||||
rm -f "$marker"
|
|
||||||
|
start_case printers
|
||||||
|
queues=$'device for Office: implicitclass://Office/\ndevice for Front_Desk: implicitclass://Front_Desk/\ndevice for all: implicitclass://all/\ndevice for Desk: ipp://192.168.1.9/ipp/print\ndevice for Attic: usb://HP/LaserJet%20P1102'
|
||||||
run_migration
|
run_migration
|
||||||
|
assert_quiet_success
|
||||||
|
|
||||||
|
grep -qxF $'pacman\t-Rs --print cups-browsed' "$log" ||
|
||||||
|
fail "the migration preflights the package removal" "$(cat "$log")"
|
||||||
grep -qxF $'sudo\tsystemctl disable --now cups-browsed.service' "$log" ||
|
grep -qxF $'sudo\tsystemctl disable --now cups-browsed.service' "$log" ||
|
||||||
fail "the removal disables and stops the discovery service" "$(cat "$log")"
|
fail "the migration disables and stops discovery" "$(cat "$log")"
|
||||||
pass "the removal disables and stops the discovery service"
|
for queue in Office Front_Desk all; do
|
||||||
|
grep -qxF "sudo lpadmin -x $queue" "$log" ||
|
||||||
# -Rns would discard /etc/cups/cups-browsed.conf rather than keep it as a
|
fail "the migration removes generated queue $queue" "$(cat "$log")"
|
||||||
# .pacsave, and a removal meant to be temporary must not delete configuration.
|
done
|
||||||
grep -qxF $'pacman\t-R --noconfirm cups-browsed' "$log" ||
|
|
||||||
fail "the removal keeps configuration pacman marked as a backup" "$(cat "$log")"
|
|
||||||
if grep -qE -- '-Rns|-Rn ' "$log"; then
|
|
||||||
fail "the removal never passes -n" "$(cat "$log")"
|
|
||||||
fi
|
|
||||||
# -s would also sweep dependencies that became unneeded, which is a promise a
|
|
||||||
# rolling dependency graph cannot keep.
|
|
||||||
if grep -q -- '-Rs --noconfirm' "$log"; then
|
|
||||||
fail "the removal names only the package it means to remove" "$(cat "$log")"
|
|
||||||
fi
|
|
||||||
pass "the removal keeps the machine's own cups-browsed.conf and touches nothing else"
|
|
||||||
|
|
||||||
# pacman deletes the unit file but not the enable symlink, and once the unit is
|
|
||||||
# gone systemd cannot resolve it by name to clean that up.
|
|
||||||
disable_line=$(grep -n 'disable --now' "$log" | tail -1 | cut -d: -f1 || true)
|
|
||||||
removal_line=$(grep -n $'^pacman\t-R --noconfirm' "$log" | head -1 | cut -d: -f1 || true)
|
|
||||||
[[ -n $disable_line && -n $removal_line ]] ||
|
|
||||||
fail "the removal both disables the unit and removes the package" "$(cat "$log")"
|
|
||||||
(( disable_line < removal_line )) ||
|
|
||||||
fail "the removal disables before the unit file goes" "$(cat "$log")"
|
|
||||||
pass "the removal disables before the unit file goes"
|
|
||||||
|
|
||||||
# cups-browsed keeps its generated queues on shutdown, and they print through
|
|
||||||
# the implicitclass backend that goes with the package.
|
|
||||||
grep -qxF $'sudo\tlpadmin -x Office' "$log" ||
|
|
||||||
fail "the queues discovery generated are removed with it" "$(cat "$log")"
|
|
||||||
grep -q 'lpadmin -x Desk' "$log" &&
|
grep -q 'lpadmin -x Desk' "$log" &&
|
||||||
fail "a printer added by hand over ipp survives" "$(cat "$log")"
|
fail "the migration leaves a manually-added IPP printer alone" "$(cat "$log")"
|
||||||
grep -q 'lpadmin -x Attic' "$log" &&
|
grep -q 'lpadmin -x Attic' "$log" &&
|
||||||
fail "a printer added by hand over usb survives" "$(cat "$log")"
|
fail "the migration leaves a manually-added USB printer alone" "$(cat "$log")"
|
||||||
grep -qxF $'sudo\tlpadmin -x Front:Desk' "$log" ||
|
grep -qxF $'omarchy-pkg-drop\tcups-browsed' "$log" ||
|
||||||
fail "a queue whose name contains a colon is still matched" "$(cat "$log")"
|
fail "the migration uses the standard package removal helper" "$(cat "$log")"
|
||||||
|
[[ -f $use_marker ]] || fail "the migration records machine-wide completion"
|
||||||
|
|
||||||
# cups-browsed names queues after what the printer advertised, so the name came
|
disable_line=$(grep -n 'disable --now' "$log" | head -1 | cut -d: -f1)
|
||||||
# off the network. lpstat and lpadmin take a destination as an option value: a
|
cleanup_line=$(grep -n 'lpadmin -x' "$log" | tail -1 | cut -d: -f1)
|
||||||
# leading dash reads as another option and a comma separates a list.
|
removal_line=$(grep -n 'omarchy-pkg-drop' "$log" | head -1 | cut -d: -f1)
|
||||||
if grep -qF -- $'lpstat\t-o -p' "$log"; then
|
(( disable_line < cleanup_line && cleanup_line < removal_line )) ||
|
||||||
fail "a queue named like an option is never passed to lpstat" "$(cat "$log")"
|
fail "the service and queues are handled before package removal" "$(cat "$log")"
|
||||||
fi
|
pass "generated queues are removed, manual printers survive, and normal output is quiet"
|
||||||
if grep -q -- 'lpadmin -x -p' "$log"; then
|
|
||||||
fail "a queue named like an option is never passed to lpadmin" "$(cat "$log")"
|
|
||||||
fi
|
|
||||||
if grep -q 'lpadmin -x a,b' "$log"; then
|
|
||||||
fail "a queue name holding a comma is never passed as a destination list" "$(cat "$log")"
|
|
||||||
fi
|
|
||||||
if grep -qF -- $'lpstat\t-o all' "$log"; then
|
|
||||||
fail "a queue named all is never asked about, since lpstat reads it as every destination" "$(cat "$log")"
|
|
||||||
fi
|
|
||||||
grep -qF -- "Cannot safely ask about jobs on the queue named '-p'" "$output" ||
|
|
||||||
fail "a queue that cannot be asked about safely is reported" "$(cat "$output")"
|
|
||||||
pass "generated queues go, hand-added printers stay, unaddressable names are reported"
|
|
||||||
|
|
||||||
# The queues have to go while cups-browsed's backend is still installed.
|
|
||||||
cleanup_line=$(grep -n 'lpadmin -x' "$log" | tail -1 | cut -d: -f1 || true)
|
|
||||||
[[ -n $cleanup_line ]] || fail "the removal cleans up generated queues" "$(cat "$log")"
|
|
||||||
(( cleanup_line < removal_line )) ||
|
|
||||||
fail "generated queues go before the backend that serves them" "$(cat "$log")"
|
|
||||||
pass "generated queues go before the backend that serves them"
|
|
||||||
|
|
||||||
[[ -f $marker ]] || fail "the removal records machine-wide completion"
|
|
||||||
pass "the removal records machine-wide completion"
|
|
||||||
|
|
||||||
# ------------------------------------ a second user, after a deliberate reinstall
|
# ------------------------------------ a second user, after a deliberate reinstall
|
||||||
|
|
||||||
# Migration state is per user. The account that runs this after someone put
|
|
||||||
# discovery back on purpose must not quietly take it away again.
|
|
||||||
installed="cups-browsed"
|
|
||||||
enabled="cups-browsed.service"
|
|
||||||
active="cups-browsed.service cups.service"
|
|
||||||
run_migration
|
run_migration
|
||||||
|
assert_quiet_success
|
||||||
[[ ! -s $log ]] || fail "a machine that already had its removal is left alone" "$(cat "$log")"
|
[[ ! -s $log ]] || fail "a completed machine-wide removal is left alone" "$(cat "$log")"
|
||||||
pass "a second user does not undo a deliberate reinstall"
|
pass "a second user does not undo a deliberate reinstall"
|
||||||
|
|
||||||
# ----------------------------------------- a machine that never had discovery
|
# ----------------------------------------- a machine that never had discovery
|
||||||
|
|
||||||
|
start_case no-package
|
||||||
installed=""
|
installed=""
|
||||||
enabled=""
|
|
||||||
active="cups.service"
|
|
||||||
use_marker="$test_tmp/var/lib/omarchy/migrations/never-had-it"
|
|
||||||
run_migration
|
run_migration
|
||||||
|
assert_quiet_success
|
||||||
[[ ! -s $log ]] || fail "a machine without discovery is left alone" "$(cat "$log")"
|
[[ ! -s $log ]] || fail "a machine without discovery is left alone" "$(cat "$log")"
|
||||||
[[ ! -e $use_marker ]] ||
|
[[ ! -e $use_marker ]] || fail "a machine without discovery gets no marker"
|
||||||
fail "a machine with nothing to remove is not made to pay for a marker" "$(cat "$log")"
|
pass "a machine without cups-browsed does no privileged work"
|
||||||
pass "a machine without discovery does no privileged work and gets no password prompt"
|
|
||||||
|
# --------------------------------------------- a machine that has no printers
|
||||||
|
|
||||||
|
start_case no-printers
|
||||||
|
run_migration
|
||||||
|
assert_quiet_success
|
||||||
|
grep -qxF $'omarchy-pkg-drop\tcups-browsed' "$log" ||
|
||||||
|
fail "no printers does not prevent package removal" "$(cat "$log")"
|
||||||
|
grep -qE 'cupsreject|lpadmin' "$log" &&
|
||||||
|
fail "no printers requires no queue administration" "$(cat "$log")"
|
||||||
|
[[ -f $use_marker ]] || fail "the no-printer migration records completion"
|
||||||
|
pass "CUPS reporting no destinations is a successful empty migration"
|
||||||
|
|
||||||
# ------------------------------------------------- a blocked package removal
|
# ------------------------------------------------- a blocked package removal
|
||||||
|
|
||||||
# Something depending on cups-browsed makes pacman refuse. Stopping the service
|
start_case blocked
|
||||||
# first and only then discovering that would leave discovery broken rather than
|
|
||||||
# removed.
|
|
||||||
installed="cups-browsed"
|
|
||||||
enabled="cups-browsed.service"
|
|
||||||
active="cups-browsed.service cups.service"
|
|
||||||
blocked="1"
|
blocked="1"
|
||||||
use_marker="$test_tmp/var/lib/omarchy/migrations/blocked"
|
|
||||||
run_migration
|
run_migration
|
||||||
|
(( status != 0 )) || fail "a blocked package removal remains pending"
|
||||||
|
assert_description_only
|
||||||
grep -q 'disable --now' "$log" &&
|
grep -q 'disable --now' "$log" &&
|
||||||
fail "a refused removal never stops the service" "$(cat "$log")"
|
fail "a blocked removal does not stop discovery" "$(cat "$log")"
|
||||||
if grep -q $'^pacman\t-R --noconfirm' "$log"; then
|
grep -q 'omarchy-pkg-drop' "$log" &&
|
||||||
fail "a refused removal does not go on to remove anything" "$(cat "$log")"
|
fail "a blocked removal does not invoke package removal" "$(cat "$log")"
|
||||||
fi
|
[[ ! -e $use_marker ]] || fail "a blocked removal gets no marker"
|
||||||
[[ ! -e $use_marker ]] || fail "a refused removal is not recorded as done"
|
pass "a package dependency prevents any partial migration"
|
||||||
# The preflight has to ask about the same command the removal will run.
|
|
||||||
grep -qxF $'pacman\t-R --print cups-browsed' "$log" ||
|
|
||||||
fail "the preflight asks pacman about the removal it will actually run" "$(cat "$log")"
|
|
||||||
pass "a removal pacman would refuse changes nothing at all"
|
|
||||||
|
|
||||||
# --------------------------------------------------- a queue that is printing
|
# --------------------------------------------------- a queue that has jobs
|
||||||
|
|
||||||
# lpadmin -x cancels the jobs on the queue it removes. A stale queue can be
|
start_case busy
|
||||||
# deleted whenever someone notices it; an aborted print cannot come back.
|
|
||||||
installed="cups-browsed"
|
|
||||||
enabled="cups-browsed.service"
|
|
||||||
active="cups-browsed.service cups.service"
|
|
||||||
blocked=""
|
|
||||||
queues=$'device for Office: implicitclass://Office/\ndevice for Spare: implicitclass://Spare/'
|
queues=$'device for Office: implicitclass://Office/\ndevice for Spare: implicitclass://Spare/'
|
||||||
busy="Office"
|
busy="Office"
|
||||||
use_marker="$test_tmp/var/lib/omarchy/migrations/printing"
|
|
||||||
run_migration
|
run_migration
|
||||||
|
assert_quiet_success
|
||||||
# The implicitclass backend only needs cups-browsed to choose a destination, so
|
grep -q 'lpadmin -x Office' "$log" &&
|
||||||
# a job already past that point finishes even though the daemon has stopped.
|
fail "a queue with jobs is not deleted" "$(cat "$log")"
|
||||||
# Deleting the queue would abort it.
|
|
||||||
if grep -q 'lpadmin -x Office' "$log"; then
|
|
||||||
fail "a queue with jobs on it is left for them to finish" "$(cat "$log")"
|
|
||||||
fi
|
|
||||||
grep -qxF $'sudo\tlpadmin -x Spare' "$log" ||
|
grep -qxF $'sudo\tlpadmin -x Spare' "$log" ||
|
||||||
fail "an idle generated queue is still removed" "$(cat "$log")"
|
fail "an idle generated queue is deleted" "$(cat "$log")"
|
||||||
|
grep -qxF $'omarchy-pkg-drop\tcups-browsed' "$log" ||
|
||||||
|
fail "a busy queue does not retain the discovery package" "$(cat "$log")"
|
||||||
|
|
||||||
# A job submitted between the check and the deletion -- the sudo in between can
|
reject_line=$(grep -n 'cupsreject.*Spare' "$log" | head -1 | cut -d: -f1)
|
||||||
# sit at a password prompt -- would be cancelled by a deletion that had decided
|
probe_line=$(grep -n $'^lpstat\t-o Spare' "$log" | head -1 | cut -d: -f1)
|
||||||
# the queue was empty.
|
delete_line=$(grep -n 'lpadmin -x Spare' "$log" | head -1 | cut -d: -f1)
|
||||||
reject_line=$(grep -n 'cupsreject.*Spare' "$log" | head -1 | cut -d: -f1 || true)
|
|
||||||
probe_line=$(grep -n $'^lpstat\t-o Spare' "$log" | head -1 | cut -d: -f1 || true)
|
|
||||||
delete_line=$(grep -n 'lpadmin -x Spare' "$log" | head -1 | cut -d: -f1 || true)
|
|
||||||
[[ -n $reject_line && -n $probe_line && -n $delete_line ]] ||
|
|
||||||
fail "a queue is closed, inspected and removed in that order" "$(cat "$log")"
|
|
||||||
(( reject_line < probe_line && probe_line < delete_line )) ||
|
(( reject_line < probe_line && probe_line < delete_line )) ||
|
||||||
fail "a queue stops taking new jobs before it is inspected or removed" "$(cat "$log")"
|
fail "a queue is closed before it is checked and deleted" "$(cat "$log")"
|
||||||
pass "a queue stops taking new jobs before it is inspected or removed"
|
pass "busy queues survive while idle discovery queues are removed"
|
||||||
|
|
||||||
grep -q 'Office still has jobs' "$output" ||
|
|
||||||
fail "a queue left alone is named so it can be removed later" "$(cat "$output")"
|
|
||||||
# One printer's job must not keep discovery on the machine.
|
|
||||||
grep -qxF $'sudo\tpacman -R --noconfirm cups-browsed' "$log" ||
|
|
||||||
fail "a busy queue does not hold up the removal" "$(cat "$log")"
|
|
||||||
pass "a queue with jobs is left for them to finish, and does not hold up the removal"
|
|
||||||
|
|
||||||
# ------------------------------------------------- a job query that fails
|
# ------------------------------------------------- a job query that fails
|
||||||
|
|
||||||
# Treating a failed query as an idle queue would delete it and abort whatever
|
start_case job-query-fails
|
||||||
# was on it, which is the one outcome this is trying to avoid.
|
queues=$'device for Office: implicitclass://Office/'
|
||||||
lpstat_o_fails="1"
|
lpstat_o_fails="1"
|
||||||
use_marker="$test_tmp/var/lib/omarchy/migrations/nojobs"
|
|
||||||
run_migration
|
run_migration
|
||||||
|
(( status != 0 )) || fail "a failed job query keeps the migration pending"
|
||||||
if grep -q 'lpadmin -x' "$log"; then
|
assert_description_only
|
||||||
fail "a queue whose jobs could not be checked is left alone" "$(cat "$log")"
|
grep -q 'lpadmin -x' "$log" &&
|
||||||
fi
|
fail "a queue with unknown job state is not deleted" "$(cat "$log")"
|
||||||
grep -q 'Could not check for jobs' "$output" ||
|
grep -q 'omarchy-pkg-drop' "$log" &&
|
||||||
fail "a job query that failed is said out loud" "$(cat "$output")"
|
fail "a failed job query retains the package" "$(cat "$log")"
|
||||||
pass "a queue whose jobs cannot be checked is left alone, not assumed idle"
|
[[ ! -e $use_marker ]] || fail "a failed job query gets no marker"
|
||||||
|
pass "a failed job query is retryable instead of falsely completing"
|
||||||
lpstat_o_fails=""
|
|
||||||
|
|
||||||
# ------------------------------------------------------ CUPS out of reach
|
# ------------------------------------------------------ CUPS out of reach
|
||||||
|
|
||||||
# Failing to reach cupsd must not read like a machine with no queues to clean.
|
start_case no-cups
|
||||||
installed="cups-browsed"
|
queues=$'device for Office: implicitclass://Office/'
|
||||||
enabled="cups-browsed.service"
|
|
||||||
active="cups-browsed.service"
|
|
||||||
busy=""
|
|
||||||
lpstat_fails="1"
|
lpstat_fails="1"
|
||||||
use_marker="$test_tmp/var/lib/omarchy/migrations/nocups"
|
|
||||||
run_migration
|
run_migration
|
||||||
|
(( status != 0 )) || fail "an unavailable CUPS server keeps the migration pending"
|
||||||
if grep -q 'lpadmin -x' "$log"; then
|
assert_description_only
|
||||||
fail "nothing is removed when the queue list could not be read" "$(cat "$log")"
|
grep -qxF "lpstat: Scheduler is not responding." "$errors" ||
|
||||||
fi
|
fail "the underlying CUPS failure is preserved" "$(cat "$errors")"
|
||||||
# Removing the backend without having read the queue list would strand every
|
grep -q 'omarchy-pkg-drop' "$log" &&
|
||||||
# generated queue permanently, and the marker would stop anyone retrying.
|
fail "an unavailable CUPS server retains the package" "$(cat "$log")"
|
||||||
if grep -q $'^pacman\t-R --noconfirm' "$log"; then
|
[[ ! -e $use_marker ]] || fail "an unavailable CUPS server gets no marker"
|
||||||
fail "the package waits until the queue list can be read" "$(cat "$log")"
|
pass "an actual CUPS failure remains pending without custom telemetry"
|
||||||
fi
|
|
||||||
[[ ! -e $use_marker ]] ||
|
|
||||||
fail "an unread queue list is not recorded as a finished removal"
|
|
||||||
grep -qi 'could not ask cups' "$output" ||
|
|
||||||
fail "a queue list that could not be read is said out loud" "$(cat "$output")"
|
|
||||||
# Stopping discovery is the half that mattered, and it is idempotent.
|
|
||||||
grep -qxF $'sudo\tsystemctl disable --now cups-browsed.service' "$log" ||
|
|
||||||
fail "discovery is still stopped when the queue list cannot be read" "$(cat "$log")"
|
|
||||||
pass "an unreadable queue list stops discovery but finalizes nothing"
|
|
||||||
|
|
||||||
lpstat_fails=""
|
|
||||||
|
|
||||||
# ------------------------------------------------ a queue that will not go
|
# ------------------------------------------------ a queue that will not go
|
||||||
|
|
||||||
# A queue left behind would route through a backend the removal is about to
|
start_case stuck-queue
|
||||||
# delete, so the package waits rather than stranding it for good.
|
|
||||||
installed="cups-browsed"
|
|
||||||
enabled="cups-browsed.service"
|
|
||||||
active="cups-browsed.service cups.service"
|
|
||||||
blocked=""
|
|
||||||
queues=$'device for Office: implicitclass://Office/'
|
queues=$'device for Office: implicitclass://Office/'
|
||||||
busy=""
|
|
||||||
lpstat_fails=""
|
|
||||||
lpadmin_fails="1"
|
lpadmin_fails="1"
|
||||||
use_marker="$test_tmp/var/lib/omarchy/migrations/stuck"
|
|
||||||
run_migration
|
run_migration
|
||||||
|
(( status != 0 )) || fail "a failed queue deletion keeps the migration pending"
|
||||||
|
grep -q 'omarchy-pkg-drop' "$log" &&
|
||||||
|
fail "a persistent generated queue retains the backend" "$(cat "$log")"
|
||||||
|
[[ ! -e $use_marker ]] || fail "a failed queue deletion gets no marker"
|
||||||
|
pass "a persistent generated queue prevents partial completion"
|
||||||
|
|
||||||
if grep -q $'^pacman\t-R --noconfirm' "$log"; then
|
# --------------------------------------------- a queue removed concurrently
|
||||||
fail "the package waits while a generated queue is still there" "$(cat "$log")"
|
|
||||||
fi
|
|
||||||
[[ ! -e $use_marker ]] || fail "a half-done cleanup is not recorded as finished"
|
|
||||||
grep -q 'Could not remove the queue Office' "$output" ||
|
|
||||||
fail "a queue that would not go is named" "$(cat "$output")"
|
|
||||||
pass "a queue that will not go keeps the package and the marker back"
|
|
||||||
|
|
||||||
lpadmin_fails=""
|
start_case vanished-queue
|
||||||
|
|
||||||
# --------------------------------------------- a queue removed by someone else
|
|
||||||
|
|
||||||
# Another administrator deleting the queue mid-run is the outcome wanted, not a
|
|
||||||
# failure worth keeping the package installed for.
|
|
||||||
installed="cups-browsed"
|
|
||||||
enabled="cups-browsed.service"
|
|
||||||
active="cups-browsed.service cups.service"
|
|
||||||
blocked=""
|
|
||||||
queues=$'device for Office: implicitclass://Office/'
|
queues=$'device for Office: implicitclass://Office/'
|
||||||
busy=""
|
|
||||||
lpstat_fails=""
|
|
||||||
lpstat_o_fails=""
|
|
||||||
lpadmin_fails="1"
|
lpadmin_fails="1"
|
||||||
gone_queues="Office"
|
gone_queues="Office"
|
||||||
use_marker="$test_tmp/var/lib/omarchy/migrations/vanished"
|
|
||||||
run_migration
|
run_migration
|
||||||
|
assert_quiet_success
|
||||||
|
grep -qxF $'omarchy-pkg-drop\tcups-browsed' "$log" ||
|
||||||
|
fail "a concurrently removed queue does not block package removal" "$(cat "$log")"
|
||||||
|
pass "a queue removed concurrently counts as removed"
|
||||||
|
|
||||||
grep -qxF $'sudo\tpacman -R --noconfirm cups-browsed' "$log" ||
|
# ------------------------------------------- a queue that cannot be closed
|
||||||
fail "a queue that is already gone does not hold up the removal" "$(cat "$log")"
|
|
||||||
[[ -f $use_marker ]] || fail "a queue that is already gone still finishes the migration"
|
|
||||||
pass "a queue someone else removed counts as removed"
|
|
||||||
|
|
||||||
lpadmin_fails=""
|
start_case open-queue
|
||||||
gone_queues=""
|
queues=$'device for Office: implicitclass://Office/'
|
||||||
|
|
||||||
# ------------------------------------------- a queue that will not stop taking jobs
|
|
||||||
|
|
||||||
# Deleting a queue that is still accepting work races whatever arrives next.
|
|
||||||
installed="cups-browsed"
|
|
||||||
enabled="cups-browsed.service"
|
|
||||||
active="cups-browsed.service cups.service"
|
|
||||||
reject_fails="1"
|
reject_fails="1"
|
||||||
use_marker="$test_tmp/var/lib/omarchy/migrations/openqueue"
|
|
||||||
run_migration
|
run_migration
|
||||||
|
(( status != 0 )) || fail "a queue that cannot be closed keeps the migration pending"
|
||||||
if grep -q 'lpadmin -x' "$log"; then
|
grep -q 'lpadmin -x' "$log" &&
|
||||||
fail "a queue still accepting jobs is not deleted" "$(cat "$log")"
|
fail "a queue still accepting jobs is not deleted" "$(cat "$log")"
|
||||||
fi
|
grep -q 'omarchy-pkg-drop' "$log" &&
|
||||||
if grep -q $'^pacman\t-R --noconfirm' "$log"; then
|
fail "a queue still accepting jobs retains the backend" "$(cat "$log")"
|
||||||
fail "the package waits while a queue is still accepting jobs" "$(cat "$log")"
|
[[ ! -e $use_marker ]] || fail "a queue still accepting jobs gets no marker"
|
||||||
fi
|
pass "a queue that cannot be closed is retried later"
|
||||||
[[ ! -e $use_marker ]] || fail "a queue still taking jobs is not recorded as done"
|
|
||||||
pass "a queue that will not stop taking jobs is neither inspected nor deleted"
|
|
||||||
|
|
||||||
reject_fails=""
|
|
||||||
|
|
||||||
# -------------------------------------------------- a masked but running daemon
|
# -------------------------------------------------- a masked but running daemon
|
||||||
|
|
||||||
installed="cups-browsed"
|
start_case masked
|
||||||
enabled=""
|
enabled=""
|
||||||
active="cups-browsed.service"
|
active="cups-browsed.service"
|
||||||
blocked=""
|
|
||||||
queues=""
|
|
||||||
use_marker="$test_tmp/var/lib/omarchy/migrations/masked"
|
|
||||||
run_migration
|
run_migration
|
||||||
|
assert_quiet_success
|
||||||
grep -qxF $'sudo\tsystemctl stop cups-browsed.service' "$log" ||
|
grep -qxF $'sudo\tsystemctl stop cups-browsed.service' "$log" ||
|
||||||
fail "a running unit is stopped even when it is not enabled" "$(cat "$log")"
|
fail "a masked running daemon is stopped" "$(cat "$log")"
|
||||||
grep -q 'disable --now' "$log" &&
|
grep -q 'disable --now' "$log" &&
|
||||||
fail "a masked unit is not disabled" "$(cat "$log")"
|
fail "a masked unit is not disabled" "$(cat "$log")"
|
||||||
pass "a masked but running daemon is stopped without being disabled"
|
pass "a masked running daemon is stopped without noisy output"
|
||||||
|
|
||||||
# A machine whose discovery never created a queue has nothing to clean up, and
|
# ------------------------------------------------ a package removal that fails
|
||||||
# no reason to reach for CUPS administration.
|
|
||||||
grep -q 'lpadmin' "$log" &&
|
start_case drop-fails
|
||||||
fail "no queues means no CUPS administration" "$(cat "$log")"
|
drop_fails="1"
|
||||||
pass "a machine with no generated queues does not touch CUPS administration"
|
run_migration
|
||||||
|
(( status != 0 )) || fail "a failed package removal keeps the migration pending"
|
||||||
|
assert_description_only
|
||||||
|
[[ ! -e $use_marker ]] || fail "a failed package removal gets no marker"
|
||||||
|
pass "a package removal failure cannot be recorded as complete"
|
||||||
|
|||||||
Reference in New Issue
Block a user