mirror of
https://github.com/jmcorgan/fips.git
synced 2026-10-05 19:18:25 +00:00
Restart fips and the gateway when an apk upgrade replaces them
apk-tools v3 runs only the incoming package's pre-upgrade and post-upgrade scripts on an upgrade, and the .apk registered neither. An upgrade replaced the binaries and init scripts on disk but left procd running the old fips and fips-gateway processes until a reboot or a manual restart. The .apk now registers pre-upgrade and post-upgrade as thin wrappers around the same prerm and postinst bodies the .ipk ships. One header line gives each body the opkg upgrade contract it already handles: pre-upgrade rewrites apk's "<new> <old>" arguments to "upgrade <new>", so prerm stops both services without disabling them and leaves its marker, and post-upgrade exports PKG_UPGRADE=1, as OpenWrt's own package-pack.mk does, so postinst starts fips and starts the gateway only if it was enabled. Registering post-upgrade alone would not have been enough: procd ignores a start of a running instance whose command line is unchanged, so the services have to be stopped first. testing/openwrt/package-test.sh runs the real build-apk.sh on the host against a stub apk and checks the registered phases, the #! lines, that the install and removal scripts are the shipped bodies, and, by executing each wrapper's header with apk's argv and environment, that the upgrade pair hands the bodies the right arguments. The ash harness runs it first and then runs the captured scripts in three new apk scenarios, and the packaging workflow's apk structural check now requires all four scripts in the adbdump. An upgrade onto a package built this way was run on OpenWrt 25.12.2 with its apk-tools 3.0.5: apk ran both pre-upgrade and post-upgrade, and both came from the incoming package. The adbdump key format the workflow check matches, each script as a "<phase>:" key under scripts:, was read from the apk-tools v3.0.5 source (src/serialize_yaml.c), the tag the packaging workflow builds from source. It matches the dump that source-built 3.0.5 printed for this change in the packaging workflow, where all four scripts appeared under scripts: and passed the check on both architectures. OpenWrt's own apk-tools 3.0.5 is built without mkpkg, and on a router adbdump cannot read the installed database and info has no --scripts, so which scripts an installed package registered cannot be read back on a device. The check covers the built package only.
This commit is contained in:
@@ -841,6 +841,17 @@ jobs:
|
||||
fi
|
||||
done
|
||||
|
||||
# Maintainer scripts. adbdump prints each registered script as a
|
||||
# "<phase>:" key under scripts:. An upgrade runs only pre-upgrade and
|
||||
# post-upgrade, so a package missing either restarts nothing.
|
||||
for s in post-install pre-upgrade post-upgrade pre-deinstall; do
|
||||
if grep -qE "^[[:space:]]*${s}:" "$DUMP"; then
|
||||
echo " PASS script: $s"
|
||||
else
|
||||
echo " FAIL script: missing $s"; fail=1
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$fail" -ne 0 ]; then
|
||||
echo "apk structural verification FAILED"
|
||||
exit 1
|
||||
|
||||
@@ -94,6 +94,21 @@ key; a single `--allow-untrusted` package install does not. If we ever publish a
|
||||
apk feed, add ECDSA (prime256v1) signing via `apk mkpkg --sign` and distribute the
|
||||
public key to `/etc/apk/keys/`.
|
||||
|
||||
## Upgrading
|
||||
|
||||
Upgrade with the same command, pointed at the new package:
|
||||
|
||||
```bash
|
||||
ssh root@192.168.1.1 apk add --allow-untrusted /tmp/fips_<new-version>_<arch>.apk
|
||||
```
|
||||
|
||||
The new package's upgrade scripts stop `fips` and `fips-gateway` before the
|
||||
files are replaced, then start `fips` again and start `fips-gateway` only if it
|
||||
was enabled, so the upgrade keeps the gateway's enabled state. apk runs
|
||||
the incoming package's upgrade scripts, not the installed one's, so this holds
|
||||
from the first upgrade onto a package that carries them, whatever version is
|
||||
installed.
|
||||
|
||||
`/etc/fips/fips.yaml` is marked as a config file (via
|
||||
`/lib/apk/packages/fips.conffiles`), so apk preserves local edits across upgrades,
|
||||
and `/lib/upgrade/keep.d/fips` preserves `/etc/fips/` across `sysupgrade` — the
|
||||
|
||||
@@ -228,16 +228,53 @@ cat > "$STAGE_DIR/lib/apk/packages/${PKG_NAME}.conffiles" <<'EOF'
|
||||
EOF
|
||||
|
||||
# ---- maintainer scripts ----
|
||||
# Map our opkg maintainer scripts onto apk's lifecycle phases:
|
||||
# opkg postinst -> apk post-install (enable + start the daemon)
|
||||
# opkg prerm -> apk pre-deinstall (stop + disable services)
|
||||
|
||||
# Both bodies come from packaging/openwrt-ipk/scripts/, the same files the
|
||||
# .ipk ships, so the two packagers cannot drift apart and testing/openwrt/
|
||||
# exercises what both install. apk runs post-install only on a fresh install,
|
||||
# so the postinst's upgrade branch is unreachable here.
|
||||
# exercises what both install. apk-tools v3 runs a different script on each
|
||||
# path, and only ever the incoming package's:
|
||||
#
|
||||
# fresh install post-install = postinst as shipped (enable + start fips;
|
||||
# the gateway stays off)
|
||||
# upgrade pre-upgrade = prerm, told it is an opkg-style upgrade;
|
||||
# runs before any file is replaced
|
||||
# post-upgrade = postinst with PKG_UPGRADE=1; runs after
|
||||
# removal pre-deinstall = prerm as shipped (stop + disable both)
|
||||
#
|
||||
# On an upgrade apk passes "<new-version> <old-version>" and a PATH-only
|
||||
# environment, which is not the contract the bodies were written for: prerm
|
||||
# would read the new version as "not an upgrade" and disable the gateway, and
|
||||
# postinst would see no PKG_UPGRADE. The upgrade pair therefore gets one header
|
||||
# line that restores opkg's contract, "upgrade <new-version>" for prerm and
|
||||
# PKG_UPGRADE=1 for postinst (OpenWrt's own package-pack.mk builds its
|
||||
# post-upgrade scripts the same way).
|
||||
#
|
||||
# Registering post-upgrade alone would not restart anything: procd treats a
|
||||
# start of a running instance with an unchanged command line as a no-op, so
|
||||
# the old binaries would keep running until a reboot. pre-upgrade stops both
|
||||
# services first, which also means a failed extraction leaves them stopped,
|
||||
# as an opkg upgrade already does.
|
||||
|
||||
wrap_script() {
|
||||
# wrap_script <header-line> <src> <dst>
|
||||
# Writes <src> to <dst> with <header-line> inserted after its #! line.
|
||||
local header="$1" src="$2" dst="$3"
|
||||
if [ "$(head -n 1 "$src")" != "#!/bin/sh" ]; then
|
||||
echo "Error: $src does not start with #!/bin/sh; cannot wrap it." >&2
|
||||
exit 1
|
||||
fi
|
||||
{
|
||||
echo "#!/bin/sh"
|
||||
echo "$header"
|
||||
tail -n +2 "$src"
|
||||
} > "$dst"
|
||||
chmod 0755 "$dst"
|
||||
}
|
||||
|
||||
install -m 0755 "$SCRIPTS_SRC/postinst" "$SCRIPTS_DIR/post-install"
|
||||
install -m 0755 "$SCRIPTS_SRC/prerm" "$SCRIPTS_DIR/pre-deinstall"
|
||||
# shellcheck disable=SC2016 # $1 is expanded by the script at run time
|
||||
wrap_script 'set -- upgrade "$1"' "$SCRIPTS_SRC/prerm" "$SCRIPTS_DIR/pre-upgrade"
|
||||
wrap_script 'export PKG_UPGRADE=1' "$SCRIPTS_SRC/postinst" "$SCRIPTS_DIR/post-upgrade"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 3. Assemble the .apk via apk mkpkg
|
||||
@@ -268,6 +305,8 @@ $FAKEROOT "$APK_BIN" mkpkg \
|
||||
--info "maintainer:FIPS Network" \
|
||||
--info "depends:$DEPENDS" \
|
||||
--script "post-install:$SCRIPTS_DIR/post-install" \
|
||||
--script "pre-upgrade:$SCRIPTS_DIR/pre-upgrade" \
|
||||
--script "post-upgrade:$SCRIPTS_DIR/post-upgrade" \
|
||||
--script "pre-deinstall:$SCRIPTS_DIR/pre-deinstall" \
|
||||
--files "$STAGE_DIR" \
|
||||
--output "$DIST_DIR/$PKG_FILENAME"
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
# Maintainer script run after the FIPS package is unpacked.
|
||||
#
|
||||
# Installed as the .ipk CONTROL/postinst and registered as the .apk
|
||||
# post-install script, so one body serves both packagers.
|
||||
# post-install script, and as the .apk post-upgrade script with PKG_UPGRADE=1
|
||||
# exported ahead of this body, so one body serves both packagers.
|
||||
#
|
||||
# The fips daemon is enabled and started on every install. The gateway is not:
|
||||
# the package ships that service disabled, and the README and the deployment
|
||||
@@ -19,8 +20,9 @@
|
||||
# re-enabled, which also re-enables one an operator had
|
||||
# disabled by hand.
|
||||
#
|
||||
# Under apk this script runs only on a fresh install, so it takes the first
|
||||
# branch and the gateway stays off.
|
||||
# Under apk, a fresh install runs this as post-install and the gateway stays
|
||||
# off. An upgrade runs it as post-upgrade, after the .apk pre-upgrade script
|
||||
# (the prerm body) has stopped the services and left the marker.
|
||||
|
||||
UPGRADE_MARKER=/tmp/fips-prerm-upgrade
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
# Maintainer script run before the FIPS package is removed or replaced.
|
||||
#
|
||||
# Installed as the .ipk CONTROL/prerm and registered as the .apk pre-deinstall
|
||||
# script, so one body serves both packagers.
|
||||
# script, and as the .apk pre-upgrade script with its arguments rewritten to
|
||||
# "upgrade <new-version>", so one body serves both packagers.
|
||||
#
|
||||
# opkg calls this with "upgrade <new-version>" when the package is being
|
||||
# replaced. Disabling the services there would erase the operator's choice,
|
||||
|
||||
@@ -31,9 +31,23 @@ if [[ ! -f "$SCRIPT_DIR/scenarios.sh" ]]; then
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# The .apk wraps the shared bodies for its upgrade path. package-test.sh builds
|
||||
# the package on the host with the real build-apk.sh, checks what it registers,
|
||||
# and leaves the four scripts here so the scenarios run exactly what ships.
|
||||
APK_DIR="$(mktemp -d)" || { echo "openwrt-scripts: mktemp failed" >&2; exit 2; }
|
||||
trap 'rm -rf "$APK_DIR"' EXIT
|
||||
bash "$SCRIPT_DIR/package-test.sh" --keep "$APK_DIR"
|
||||
rc=$?
|
||||
if [[ $rc -ne 0 ]]; then
|
||||
echo "openwrt-scripts: package-test.sh exited $rc" >&2
|
||||
exit $rc
|
||||
fi
|
||||
|
||||
docker run --rm --network none \
|
||||
-v "$PROJECT_ROOT:/src:ro" \
|
||||
-v "$APK_DIR:/apk:ro" \
|
||||
-e REPO=/src \
|
||||
-e APK_SCRIPTS=/apk \
|
||||
-e "POSTINST=${POSTINST:-}" \
|
||||
-e "PRERM=${PRERM:-}" \
|
||||
"$IMAGE" sh /src/testing/openwrt/scenarios.sh
|
||||
|
||||
Executable
+240
@@ -0,0 +1,240 @@
|
||||
#!/bin/bash
|
||||
# ── OpenWrt package contents, checked on the host ───────────────────────────
|
||||
# Runs the real packaging/openwrt-apk/build-apk.sh against placeholder
|
||||
# binaries and a stub `apk` that records what `apk mkpkg` was asked to
|
||||
# package. No docker, no apk-tools and no FIPS build are needed.
|
||||
#
|
||||
# What it checks is which maintainer scripts the .apk registers and what each
|
||||
# one does with apk's upgrade arguments and environment (apk-tools v3 passes
|
||||
# "<new-version> <old-version>" and a PATH-only environment to pre-upgrade and
|
||||
# post-upgrade). Whether a real `apk mkpkg` accepts the result is left to the
|
||||
# GitHub packaging workflow, which builds with the real tool.
|
||||
#
|
||||
# Usage: package-test.sh [--keep <dir>]
|
||||
# --keep <dir> copy the captured apk scripts into <dir> as post-install,
|
||||
# pre-upgrade, post-upgrade and pre-deinstall, so
|
||||
# scenarios.sh can run them under ash.
|
||||
#
|
||||
# Exit 0 = every check passed. Exit 1 = at least one failed. Exit 2 = the
|
||||
# harness could not run; never treated as a pass.
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
set -uo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
SCRIPTS_SRC="$PROJECT_ROOT/packaging/openwrt-ipk/scripts"
|
||||
|
||||
KEEP=""
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--keep)
|
||||
[[ $# -ge 2 ]] || { echo "package-test: --keep needs a directory" >&2; exit 2; }
|
||||
KEEP="$2"
|
||||
shift 2
|
||||
;;
|
||||
*) echo "package-test: unknown argument: $1" >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
if [[ -n "$KEEP" && ! -d "$KEEP" ]]; then
|
||||
echo "package-test: --keep needs an existing directory" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# The version is unique to this run so the cleanup below removes only what
|
||||
# this run's builders wrote into dist/.
|
||||
PKG_VERSION="pkgtest.$$"
|
||||
TMP="$(mktemp -d)" || { echo "package-test: mktemp failed" >&2; exit 2; }
|
||||
|
||||
trap 'rm -rf "$TMP"; rm -f "$PROJECT_ROOT/dist/fips_${PKG_VERSION}_"*' EXIT
|
||||
|
||||
harness_fail() {
|
||||
echo "package-test: $*" >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
FAILURES=0
|
||||
CASES=0
|
||||
|
||||
ok() {
|
||||
CASES=$((CASES + 1))
|
||||
echo " ok $*"
|
||||
return 0
|
||||
}
|
||||
|
||||
bad() {
|
||||
CASES=$((CASES + 1))
|
||||
FAILURES=$((FAILURES + 1))
|
||||
echo " FAIL $*"
|
||||
return 0
|
||||
}
|
||||
|
||||
# ── Build the .apk against a stub apk ───────────────────────────────────────
|
||||
|
||||
BINS="$TMP/bins"
|
||||
CAPTURE="$TMP/capture"
|
||||
mkdir -p "$BINS" "$CAPTURE" || harness_fail "cannot create $TMP subdirectories"
|
||||
for bin in fips fipsctl fipstop fips-gateway; do
|
||||
printf 'x' > "$BINS/$bin" || harness_fail "cannot write placeholder $bin"
|
||||
done
|
||||
|
||||
# The stub knows only the arguments build-apk.sh passes today. Anything else
|
||||
# exits 64, so a new mkpkg argument fails the build rather than going unseen.
|
||||
cat > "$TMP/apk" <<STUB
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
cap='$CAPTURE'
|
||||
[ "\${1-}" = mkpkg ] || { echo "stub apk: only mkpkg is supported, got '\${1-}'" >&2; exit 64; }
|
||||
shift
|
||||
files=""
|
||||
out=""
|
||||
while [ \$# -gt 0 ]; do
|
||||
[ \$# -ge 2 ] || { echo "stub apk: \$1 has no value" >&2; exit 64; }
|
||||
case "\$1" in
|
||||
--info) ;;
|
||||
--script)
|
||||
phase="\${2%%:*}"
|
||||
cp "\${2#*:}" "\$cap/script.\$phase"
|
||||
echo "\$phase" >> "\$cap/phases"
|
||||
;;
|
||||
--files) files="\$2" ;;
|
||||
--output) out="\$2" ;;
|
||||
*) echo "stub apk: unknown argument \$1" >&2; exit 64 ;;
|
||||
esac
|
||||
shift 2
|
||||
done
|
||||
[ -n "\$files" ] && [ -n "\$out" ] || { echo "stub apk: --files and --output are required" >&2; exit 64; }
|
||||
(cd "\$files" && find . -mindepth 1 | LC_ALL=C sort) > "\$cap/payload"
|
||||
: > "\$out"
|
||||
: > "\$cap/called"
|
||||
STUB
|
||||
chmod 0755 "$TMP/apk" || harness_fail "cannot make the stub apk executable"
|
||||
|
||||
echo "OpenWrt package checks"
|
||||
echo "==> build-apk.sh with a stub apk"
|
||||
if ! PKG_VERSION="$PKG_VERSION" APK_VERSION=0.0.0-r0 APK_BIN="$TMP/apk" \
|
||||
bash "$PROJECT_ROOT/packaging/openwrt-apk/build-apk.sh" --arch x86_64 --bin-dir "$BINS" \
|
||||
> "$TMP/build-apk.log" 2>&1; then
|
||||
cat "$TMP/build-apk.log" >&2
|
||||
harness_fail "build-apk.sh failed, so nothing was checked"
|
||||
fi
|
||||
[[ -f "$CAPTURE/called" ]] || harness_fail "build-apk.sh exited 0 but never ran apk mkpkg"
|
||||
[[ -f "$CAPTURE/phases" ]] || harness_fail "apk mkpkg ran but no script phase was recorded"
|
||||
|
||||
# ── A1. The .apk registers exactly the four lifecycle scripts ───────────────
|
||||
# apk runs only post-install on a fresh install, only pre-upgrade and
|
||||
# post-upgrade on an upgrade, and only pre-deinstall on a removal.
|
||||
WANT_PHASES="post-install post-upgrade pre-deinstall pre-upgrade"
|
||||
got_phases="$(LC_ALL=C sort "$CAPTURE/phases" | tr '\n' ' ')"
|
||||
got_phases="${got_phases% }"
|
||||
if [[ "$got_phases" == "$WANT_PHASES" ]]; then
|
||||
ok "A1 the .apk registers $WANT_PHASES"
|
||||
else
|
||||
missing=""
|
||||
for phase in $WANT_PHASES; do
|
||||
grep -qxF "$phase" "$CAPTURE/phases" || missing="$missing $phase"
|
||||
done
|
||||
bad "A1 registered phases are '$got_phases', want '$WANT_PHASES'; missing:${missing:- none}"
|
||||
fi
|
||||
|
||||
# ── A2. Every registered script starts with a working #! line ───────────────
|
||||
# apk execs the script directly, so the kernel reads line 1.
|
||||
for phase in $WANT_PHASES; do
|
||||
script="$CAPTURE/script.$phase"
|
||||
if [[ ! -f "$script" ]]; then
|
||||
bad "A2 $phase is not registered, so it has no #! line"
|
||||
elif [[ "$(head -n 1 "$script")" == "#!/bin/sh" ]]; then
|
||||
ok "A2 $phase starts with #!/bin/sh"
|
||||
else
|
||||
bad "A2 $phase starts with '$(head -n 1 "$script")', not #!/bin/sh"
|
||||
fi
|
||||
done
|
||||
|
||||
# ── A3. Install and removal ship the shared bodies unchanged ────────────────
|
||||
for pair in post-install:postinst pre-deinstall:prerm; do
|
||||
phase="${pair%%:*}"
|
||||
src="$SCRIPTS_SRC/${pair#*:}"
|
||||
if cmp -s "$CAPTURE/script.$phase" "$src"; then
|
||||
ok "A3 $phase is the shipped ${pair#*:}, byte for byte"
|
||||
else
|
||||
bad "A3 $phase differs from the shipped ${pair#*:}"
|
||||
fi
|
||||
done
|
||||
|
||||
# ── A4 and A5. The upgrade pair wraps the shared bodies ─────────────────────
|
||||
# A4 reads the structure: the script ends with the body after its #! line, and
|
||||
# at least one header line sits between the two. A5 runs the header with apk's
|
||||
# upgrade argv and environment, so what is judged is what the shell does with
|
||||
# it, not how it reads.
|
||||
for pair in pre-upgrade:prerm post-upgrade:postinst; do
|
||||
phase="${pair%%:*}"
|
||||
src="$SCRIPTS_SRC/${pair#*:}"
|
||||
script="$CAPTURE/script.$phase"
|
||||
if [[ ! -f "$script" ]]; then
|
||||
bad "A4 $phase is not registered"
|
||||
bad "A5 $phase is not registered, so its header cannot run"
|
||||
continue
|
||||
fi
|
||||
|
||||
tail -n +2 "$src" > "$TMP/body" || harness_fail "cannot read $src"
|
||||
body_lines=$(wc -l < "$TMP/body")
|
||||
script_lines=$(wc -l < "$script")
|
||||
header_lines=$((script_lines - body_lines))
|
||||
if [[ $header_lines -lt 2 ]]; then
|
||||
bad "A4 $phase has $header_lines header line(s) before the ${pair#*:} body, want at least 2"
|
||||
elif ! tail -n "$body_lines" "$script" | cmp -s - "$TMP/body"; then
|
||||
bad "A4 $phase does not end with the ${pair#*:} body"
|
||||
else
|
||||
ok "A4 $phase is a $header_lines-line header and the ${pair#*:} body"
|
||||
fi
|
||||
|
||||
if [[ $header_lines -lt 1 ]]; then
|
||||
bad "A5 $phase has no header to run"
|
||||
continue
|
||||
fi
|
||||
probe="$TMP/probe.$phase"
|
||||
{
|
||||
head -n "$header_lines" "$script"
|
||||
# shellcheck disable=SC2016
|
||||
printf '%s\n' 'printf '\''%s|%s|%s\n'\'' "${1-}" "${2-}" "${PKG_UPGRADE-}"'
|
||||
} > "$probe"
|
||||
chmod 0755 "$probe" || harness_fail "cannot make the $phase probe executable"
|
||||
got="$(env -i PATH=/usr/sbin:/usr/bin:/sbin:/bin "$probe" 0.6.0-r1 0.5.2-r1)"
|
||||
rc=$?
|
||||
if [[ $rc -eq 126 || $rc -eq 127 ]]; then
|
||||
harness_fail "the $phase probe could not be executed (exit $rc)"
|
||||
fi
|
||||
IFS='|' read -r f1 f2 f3 <<< "$got"
|
||||
case "$phase" in
|
||||
pre-upgrade)
|
||||
if [[ $rc -eq 0 && "$f1|$f2" == "upgrade|0.6.0-r1" ]]; then
|
||||
ok "A5 pre-upgrade hands the body 'upgrade 0.6.0-r1'"
|
||||
else
|
||||
bad "A5 pre-upgrade hands the body '$f1 $f2' (exit $rc), want 'upgrade 0.6.0-r1'"
|
||||
fi
|
||||
;;
|
||||
post-upgrade)
|
||||
if [[ $rc -eq 0 && "$f3" == "1" ]]; then
|
||||
ok "A5 post-upgrade runs the body with PKG_UPGRADE=1"
|
||||
else
|
||||
bad "A5 post-upgrade runs the body with PKG_UPGRADE='$f3' (exit $rc), want 1"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ── Hand the scripts to the ash scenarios ───────────────────────────────────
|
||||
if [[ -n "$KEEP" ]]; then
|
||||
for phase in $WANT_PHASES; do
|
||||
[[ -f "$CAPTURE/script.$phase" ]] || continue
|
||||
install -m 0755 "$CAPTURE/script.$phase" "$KEEP/$phase" \
|
||||
|| harness_fail "cannot copy $phase into $KEEP"
|
||||
done
|
||||
fi
|
||||
|
||||
echo ""
|
||||
if [[ $FAILURES -eq 0 ]]; then
|
||||
echo "package-test: all $CASES checks passed"
|
||||
exit 0
|
||||
fi
|
||||
echo "package-test: $FAILURES of $CASES checks failed"
|
||||
exit 1
|
||||
@@ -11,6 +11,13 @@
|
||||
# POSTINST and PRERM may be pointed at other files. That is the seam used to
|
||||
# see a scenario red against the previously released scripts, and to re-break
|
||||
# the fixed ones during a break-check.
|
||||
#
|
||||
# APK_SCRIPTS names a directory holding the four scripts the .apk registers
|
||||
# (post-install, pre-upgrade, post-upgrade, pre-deinstall), as captured from
|
||||
# the real build-apk.sh by package-test.sh --keep. Scenarios 8 to 10 execute
|
||||
# them directly with apk-tools v3's argv and PATH-only environment, so the
|
||||
# kernel reads their #! line and ash runs them. A missing directory or script
|
||||
# fails those scenarios; it is never a skip.
|
||||
|
||||
set -u
|
||||
|
||||
@@ -19,6 +26,7 @@ POSTINST="${POSTINST:-$REPO/packaging/openwrt-ipk/scripts/postinst}"
|
||||
PRERM="${PRERM:-$REPO/packaging/openwrt-ipk/scripts/prerm}"
|
||||
RELEASED_PRERM="$REPO/testing/openwrt/fixtures/released-prerm"
|
||||
INIT_GATEWAY="$REPO/packaging/openwrt-ipk/files/etc/init.d/fips-gateway"
|
||||
APK_SCRIPTS="${APK_SCRIPTS:-}"
|
||||
SHIPPED_YAML="$REPO/packaging/openwrt-ipk/files/etc/fips/fips.yaml"
|
||||
|
||||
WORK=/tmp/fips-openwrt-scenarios
|
||||
@@ -146,6 +154,52 @@ assert_absent() {
|
||||
return 0
|
||||
}
|
||||
|
||||
assert_present() {
|
||||
if [ -e "$1" ]; then
|
||||
ok "$2"
|
||||
else
|
||||
bad "$2 — $1 does not exist"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
first_call_line() {
|
||||
grep -nxF "$1" "$CALLS" | head -n 1 | cut -d: -f1
|
||||
return 0
|
||||
}
|
||||
|
||||
assert_order() {
|
||||
# assert_order <first call> <second call> <what it means>
|
||||
first="$(first_call_line "$1")"
|
||||
second="$(first_call_line "$2")"
|
||||
if [ -z "$first" ] || [ -z "$second" ]; then
|
||||
bad "$3 — '$1' and '$2' were not both called: $(calls_oneline)"
|
||||
elif [ "$first" -lt "$second" ]; then
|
||||
ok "$3"
|
||||
else
|
||||
bad "$3 — '$2' came before '$1': $(calls_oneline)"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
run_apk_script() {
|
||||
# run_apk_script <phase> <args...>
|
||||
# Runs one captured .apk script the way apk-tools v3 does: executed
|
||||
# directly, with only PATH in the environment. The stubs' own state
|
||||
# variables are passed through so they can record the calls.
|
||||
phase="$1"
|
||||
shift
|
||||
script="$APK_SCRIPTS/$phase"
|
||||
if [ -z "$APK_SCRIPTS" ] || [ ! -x "$script" ]; then
|
||||
bad "the .apk $phase script is not available at '$script'"
|
||||
return 1
|
||||
fi
|
||||
env -i PATH=/usr/sbin:/usr/bin:/sbin:/bin \
|
||||
CALLS="$CALLS" GW_STATE="$GW_STATE" FIPS_STATE="$FIPS_STATE" \
|
||||
"$script" "$@" >/dev/null 2>&1
|
||||
return 0
|
||||
}
|
||||
|
||||
# ── 1. Fresh install ────────────────────────────────────────────────────────
|
||||
# opkg runs the postinst with "configure"; PKG_UPGRADE is set only on upgrades,
|
||||
# so both its absence and an explicit 0 must leave the gateway alone.
|
||||
@@ -321,9 +375,69 @@ YAML
|
||||
return 0
|
||||
}
|
||||
|
||||
# ── 8. apk fresh install ────────────────────────────────────────────────────
|
||||
# apk-tools v3 runs only post-install, with the new version as its argument.
|
||||
scenario_apk_fresh_install() {
|
||||
note "scenario 8: apk fresh install"
|
||||
reset_state
|
||||
|
||||
run_apk_script post-install 0.6.0-r1 || return 0
|
||||
|
||||
assert_called "fips enable" "an apk install enables the daemon"
|
||||
assert_called "fips start" "an apk install starts the daemon"
|
||||
assert_not_called "fips-gateway enable" "an apk install does not enable the gateway"
|
||||
assert_not_called "fips-gateway start" "an apk install does not start the gateway"
|
||||
assert_file_is "$GW_STATE" "0" "an apk install leaves the gateway disabled"
|
||||
return 0
|
||||
}
|
||||
|
||||
# ── 9. apk upgrade, gateway enabled ─────────────────────────────────────────
|
||||
# apk-tools v3 runs only the new package's pre-upgrade and post-upgrade, with
|
||||
# "<new-version> <old-version>"; the old package runs nothing.
|
||||
scenario_apk_upgrade_enabled() {
|
||||
note "scenario 9: apk upgrade, gateway enabled"
|
||||
reset_state
|
||||
echo 1 > "$GW_STATE"
|
||||
echo 1 > "$FIPS_STATE"
|
||||
|
||||
run_apk_script pre-upgrade 0.6.0-r1 0.5.2-r1 || return 0
|
||||
assert_called "fips-gateway stop" "pre-upgrade stops the gateway"
|
||||
assert_called "fips stop" "pre-upgrade stops the daemon"
|
||||
assert_not_called "fips-gateway disable" "pre-upgrade does not disable the gateway"
|
||||
assert_not_called "fips disable" "pre-upgrade does not disable the daemon"
|
||||
assert_file_is "$GW_STATE" "1" "the gateway is still enabled after pre-upgrade"
|
||||
assert_present "$UPGRADE_MARKER" "pre-upgrade leaves the upgrade marker"
|
||||
|
||||
run_apk_script post-upgrade 0.6.0-r1 0.5.2-r1 || return 0
|
||||
assert_order "fips stop" "fips start" "the daemon is started again after it was stopped"
|
||||
assert_order "fips-gateway stop" "fips-gateway start" "the gateway is started again after it was stopped"
|
||||
assert_not_called "fips-gateway enable" "an enabled gateway does not need re-enabling"
|
||||
assert_file_is "$GW_STATE" "1" "the gateway stays enabled across the apk upgrade"
|
||||
assert_absent "$UPGRADE_MARKER" "post-upgrade removes the upgrade marker"
|
||||
return 0
|
||||
}
|
||||
|
||||
# ── 10. apk upgrade, gateway disabled ───────────────────────────────────────
|
||||
scenario_apk_upgrade_disabled() {
|
||||
note "scenario 10: apk upgrade, gateway disabled"
|
||||
reset_state
|
||||
echo 1 > "$FIPS_STATE"
|
||||
|
||||
run_apk_script pre-upgrade 0.6.0-r1 0.5.2-r1 || return 0
|
||||
run_apk_script post-upgrade 0.6.0-r1 0.5.2-r1 || return 0
|
||||
|
||||
assert_order "fips stop" "fips start" "the daemon is started again after it was stopped"
|
||||
assert_file_is "$GW_STATE" "0" "a disabled gateway stays disabled across the apk upgrade"
|
||||
assert_not_called "fips-gateway enable" "a disabled gateway is not enabled by the apk upgrade"
|
||||
assert_not_called "fips-gateway start" "a disabled gateway is not started by the apk upgrade"
|
||||
assert_absent "$UPGRADE_MARKER" "post-upgrade removes the upgrade marker"
|
||||
return 0
|
||||
}
|
||||
|
||||
echo "OpenWrt maintainer-script scenarios (shell: $(readlink -f /proc/$$/exe 2>/dev/null || echo sh))"
|
||||
echo " postinst: $POSTINST"
|
||||
echo " prerm: $PRERM"
|
||||
echo " apk: ${APK_SCRIPTS:-(not set)}"
|
||||
|
||||
scenario_fresh_install
|
||||
scenario_upgrade_from_released
|
||||
@@ -332,6 +446,9 @@ scenario_upgrade_disabled
|
||||
scenario_removal
|
||||
scenario_config_reader
|
||||
scenario_start_service_guard
|
||||
scenario_apk_fresh_install
|
||||
scenario_apk_upgrade_enabled
|
||||
scenario_apk_upgrade_disabled
|
||||
|
||||
echo ""
|
||||
if [ "$FAILURES" -eq 0 ]; then
|
||||
|
||||
Reference in New Issue
Block a user