refresh Phase 2A transaction and readiness gates
This commit is contained in:
parent
2cf249d6f2
commit
da03be61a9
23 changed files with 2409 additions and 37 deletions
|
|
@ -10,7 +10,10 @@ fail() {
|
|||
manifest_value() {
|
||||
key=$1
|
||||
file=$2
|
||||
value=$(awk -F= -v k="$key" '$1==k {sub(/^[^=]*=/, ""); print; found++} END {if(found!=1) exit 1}' "$file")
|
||||
count=$(awk -F= -v k="$key" '$1==k {found++} END {print found+0}' "$file")
|
||||
rc=$?
|
||||
if [ "$rc" -ne 0 ] || [ "$count" != 1 ]; then return 1; fi
|
||||
value=$(awk -F= -v k="$key" '$1==k {sub(/^[^=]*=/, ""); print}' "$file")
|
||||
rc=$?
|
||||
if [ "$rc" -ne 0 ]; then return "$rc"; fi
|
||||
printf '%s\n' "$value"
|
||||
|
|
@ -89,11 +92,130 @@ validate_health_url_file() {
|
|||
return 0
|
||||
}
|
||||
|
||||
rerun_volatile_checks() {
|
||||
systemctl --failed --no-legend --plain --no-pager > "$record/volatile-failed-units.txt" 2>&1
|
||||
capture_failed_unit_fingerprint() {
|
||||
output=$1
|
||||
: > "$output"
|
||||
systemctl --failed --no-legend --plain --no-pager | awk '{print $1}' | LC_ALL=C sort |
|
||||
while IFS= read -r unit; do
|
||||
[ -n "$unit" ] || continue
|
||||
printf '%s|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s\n' \
|
||||
"$(systemctl show "$unit" -p Id --value --no-pager)" \
|
||||
"$(systemctl show "$unit" -p Type --value --no-pager)" \
|
||||
"$(systemctl show "$unit" -p LoadState --value --no-pager)" \
|
||||
"$(systemctl show "$unit" -p ActiveState --value --no-pager)" \
|
||||
"$(systemctl show "$unit" -p SubState --value --no-pager)" \
|
||||
"$(systemctl show "$unit" -p Result --value --no-pager)" \
|
||||
"$(systemctl show "$unit" -p InvocationID --value --no-pager)" \
|
||||
"$(systemctl show "$unit" -p StateChangeTimestamp --value --no-pager)" \
|
||||
"$(systemctl show "$unit" -p FragmentPath --value --no-pager)" \
|
||||
"$(systemctl show "$unit" -p ExecMainCode --value --no-pager)" \
|
||||
"$(systemctl show "$unit" -p ExecMainStatus --value --no-pager)"
|
||||
done > "$output"
|
||||
return ${PIPESTATUS[0]}
|
||||
}
|
||||
|
||||
verify_failed_unit_disposition() {
|
||||
output=$1
|
||||
(cd "$script_dir/failed-unit-disposition" && sha256sum -c SHA256SUMS) \
|
||||
> "$record/failed-unit-allowlist-checksum-latest.txt" 2>&1
|
||||
rc=$?
|
||||
[ "$rc" -eq 0 ] || { fail 'cannot capture current failed units' "$rc"; return $?; }
|
||||
[ ! -s "$record/volatile-failed-units.txt" ] || { fail 'one or more failed units are present' 45; return $?; }
|
||||
[ "$rc" -eq 0 ] || { fail 'failed-unit allowlist checksum failed' 45; return $?; }
|
||||
capture_failed_unit_fingerprint "$output"
|
||||
rc=$?
|
||||
[ "$rc" -eq 0 ] || { fail 'cannot fingerprint current failed units' "$rc"; return $?; }
|
||||
diff -u "$script_dir/failed-unit-disposition/drkonqi-failed-units.allowlist.psv" "$output" \
|
||||
> "$record/failed-unit-disposition-latest.diff" 2>&1
|
||||
rc=$?
|
||||
[ "$rc" -eq 0 ] || { fail 'failed-unit set, state, invocation ID, or fingerprint changed' 45; return $?; }
|
||||
count=$(wc -l < "$output")
|
||||
[ "$count" = 234 ] || { fail "failed-unit count changed: expected=234 actual=$count" 45; return $?; }
|
||||
return 0
|
||||
}
|
||||
|
||||
verify_snapshot_subvolume() {
|
||||
number=$1
|
||||
description=$2
|
||||
snapshot_dir=/.snapshots/$number
|
||||
snapshot_subvolume=$snapshot_dir/snapshot
|
||||
[ -d "$snapshot_dir" ] || { fail "Snapper numbered directory missing: $snapshot_dir" 50; return $?; }
|
||||
btrfs subvolume show "$snapshot_subvolume" > "$record/pre-upgrade-snapshot-subvolume.txt" 2>&1
|
||||
rc=$?
|
||||
[ "$rc" -eq 0 ] || { fail "Snapper snapshot is not a verified Btrfs subvolume: $snapshot_subvolume" 50; return $?; }
|
||||
snapper list --columns number,type,pre-number,date,user,cleanup,description \
|
||||
> "$record/snapper-snapshots-after-explicit-create.txt" 2>&1
|
||||
rc=$?
|
||||
[ "$rc" -eq 0 ] || { fail 'cannot verify explicit snapshot through Snapper' "$rc"; return $?; }
|
||||
awk -v number="$number" -v description="$description" \
|
||||
'$1 == number && index($0, description) {print}' \
|
||||
"$record/snapper-snapshots-after-explicit-create.txt" \
|
||||
> "$record/pre-upgrade-snapshot-list-match.txt" 2>&1
|
||||
rc=$?
|
||||
if [ "$rc" -ne 0 ] || [ ! -s "$record/pre-upgrade-snapshot-list-match.txt" ]; then
|
||||
fail 'explicit snapshot number and description not found together in Snapper list' 50
|
||||
return $?
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
create_explicit_pre_upgrade_snapshot() {
|
||||
btrfs subvolume show /.snapshots > "$record/snapshots-subvolume-before-create.txt" 2>&1
|
||||
rc=$?
|
||||
[ "$rc" -eq 0 ] || { fail '/.snapshots is not a verified Btrfs subvolume' "$rc"; return $?; }
|
||||
snapper list --columns number > "$record/snapper-numbers-before-explicit.txt" 2>&1
|
||||
rc=$?
|
||||
[ "$rc" -eq 0 ] || { fail 'cannot capture Snapper number baseline' "$rc"; return $?; }
|
||||
grep -E '^[[:space:]]*[0-9]+[[:space:]]*$' "$record/snapper-numbers-before-explicit.txt" | tr -d '[:space:]' | LC_ALL=C sort \
|
||||
> "$record/snapper-numbers-before-explicit.normalized"
|
||||
description="le-phase2a-pre-upgrade-$(date -u +%Y%m%dT%H%M%SZ)-$(basename "$record")"
|
||||
snapshot_output=$(snapper create --type single --cleanup-algorithm number \
|
||||
--description "$description" --print-number 2> "$record/pre-upgrade-snapshot-create.err")
|
||||
rc=$?
|
||||
printf '%s\n' "$snapshot_output" > "$record/pre-upgrade-snapshot-create.out"
|
||||
[ "$rc" -eq 0 ] || { fail "explicit pre-upgrade Snapper snapshot failed rc=$rc" 50; return $?; }
|
||||
snapshot_number=$(printf '%s' "$snapshot_output" | tr -d '[:space:]')
|
||||
case "$snapshot_number" in ''|*[!0-9]*) fail 'Snapper returned an invalid snapshot number' 50; return $? ;; esac
|
||||
[ "$snapshot_number" -gt 0 ] || { fail 'Snapper returned nonpositive snapshot number' 50; return $?; }
|
||||
verify_snapshot_subvolume "$snapshot_number" "$description" || return $?
|
||||
{
|
||||
printf 'snapshot_number=%s\n' "$snapshot_number"
|
||||
printf 'description=%s\n' "$description"
|
||||
printf 'snapshot_directory=/.snapshots/%s\n' "$snapshot_number"
|
||||
printf 'snapshot_subvolume=/.snapshots/%s/snapshot\n' "$snapshot_number"
|
||||
} > "$record/pre-upgrade-snapshot.manifest"
|
||||
btrfs qgroup show / > "$record/btrfs-quota-observation-before-upgrade.txt" 2>&1
|
||||
rc=$?
|
||||
if [ "$rc" -ne 0 ]; then
|
||||
printf 'btrfs-quota-accounting|unavailable-or-disabled|rc=%s|explicit-snapshot-still-verified\n' "$rc" \
|
||||
>> "$record/warnings-before-upgrade.txt"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
verify_snap_pac_snapshots() {
|
||||
snapper list --columns number > "$record/snapper-numbers-after-transaction.txt" 2>&1
|
||||
rc=$?
|
||||
[ "$rc" -eq 0 ] || { fail 'cannot capture post-transaction Snapper numbers' "$rc"; return $?; }
|
||||
grep -E '^[[:space:]]*[0-9]+[[:space:]]*$' "$record/snapper-numbers-after-transaction.txt" | tr -d '[:space:]' | LC_ALL=C sort \
|
||||
> "$record/snapper-numbers-after-transaction.normalized"
|
||||
comm -13 "$record/snapper-numbers-before-explicit.normalized" "$record/snapper-numbers-after-transaction.normalized" \
|
||||
> "$record/snapper-new-numbers.txt"
|
||||
new_count=$(wc -l < "$record/snapper-new-numbers.txt")
|
||||
if [ "$new_count" -lt 3 ]; then
|
||||
fail "expected explicit plus snap-pac pre/post snapshots; observed new snapshot count=$new_count" 51
|
||||
return $?
|
||||
fi
|
||||
while IFS= read -r number; do
|
||||
case "$number" in ''|*[!0-9]*) fail 'invalid new Snapper snapshot number' 51; return $? ;; esac
|
||||
btrfs subvolume show "/.snapshots/$number/snapshot" \
|
||||
> "$record/snapper-new-subvolume-$number.txt" 2>&1
|
||||
rc=$?
|
||||
[ "$rc" -eq 0 ] || { fail "new Snapper snapshot $number is not a Btrfs subvolume" 51; return $?; }
|
||||
done < "$record/snapper-new-numbers.txt"
|
||||
return 0
|
||||
}
|
||||
|
||||
rerun_volatile_checks() {
|
||||
verify_failed_unit_disposition "$record/volatile-failed-units-fingerprint.psv" || return $?
|
||||
pacman -Dk > "$record/volatile-pacman-Dk.txt" 2>&1
|
||||
rc=$?
|
||||
[ "$rc" -eq 0 ] || { fail 'package database consistency failed immediately before execution' "$rc"; return $?; }
|
||||
|
|
@ -161,6 +283,7 @@ main() {
|
|||
fi
|
||||
validate_preflight "$2" || return $?
|
||||
validate_health_url_file "$validated_preflight/health-urls.sanitized" || return $?
|
||||
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P)
|
||||
|
||||
umask 077
|
||||
install -d -o root -g root -m 0700 /var/log/le-phase2a-maintenance
|
||||
|
|
@ -179,15 +302,14 @@ main() {
|
|||
rc=$?
|
||||
[ "$rc" -eq 0 ] || { fail 'cannot copy validated preflight record' "$rc"; return $?; }
|
||||
|
||||
report=/tmp/le-phase2-package-decision-20260712.Mn7mIx/phase2-package-decision-report.md
|
||||
report_manifest=/tmp/le-phase2-package-decision-20260712.Mn7mIx/SHA256SUMS.main
|
||||
printf '%s %s\n' 195d74afe121556294318d0ed49e3fb5504088dbb31a4c0786b8adea44f1369d "$report" | sha256sum -c - \
|
||||
approved_transaction=$script_dir/approved-transaction
|
||||
(cd "$approved_transaction" && sha256sum -c SHA256SUMS) \
|
||||
> "$record/approved-report-checksum.txt" 2>&1
|
||||
rc=$?
|
||||
[ "$rc" -eq 0 ] || { fail 'approved report checksum failed' "$rc"; return $?; }
|
||||
cp "$report" "$report_manifest" "$record/"
|
||||
[ "$rc" -eq 0 ] || { fail 'approved transaction checksum failed' "$rc"; return $?; }
|
||||
cp -a "$approved_transaction" "$record/approved-transaction"
|
||||
rc=$?
|
||||
[ "$rc" -eq 0 ] || { fail 'cannot copy approved report and checksum manifest' "$rc"; return $?; }
|
||||
[ "$rc" -eq 0 ] || { fail 'cannot copy approved transaction evidence' "$rc"; return $?; }
|
||||
|
||||
uname -a > "$record/running-kernel-before.txt" 2>&1
|
||||
rc=$?; [ "$rc" -eq 0 ] || { fail 'cannot capture running kernel' "$rc"; return $?; }
|
||||
|
|
@ -199,9 +321,9 @@ main() {
|
|||
"$validated_preflight/running-containers-before.txt" "$validated_preflight/containers-all.psv" \
|
||||
"$validated_preflight/critical-units-baseline.psv" "$validated_preflight/database-baseline.psv" "$record/"
|
||||
rc=$?; [ "$rc" -eq 0 ] || { fail 'cannot copy essential rollback baseline evidence' "$rc"; return $?; }
|
||||
systemctl --failed --no-legend --plain --no-pager > "$record/failed-units-before.txt" 2>&1
|
||||
rc=$?; [ "$rc" -eq 0 ] || { fail 'cannot capture failed-unit baseline' "$rc"; return $?; }
|
||||
[ ! -s "$record/failed-units-before.txt" ] || { fail 'failed units appeared before transaction preview' 45; return $?; }
|
||||
verify_failed_unit_disposition "$record/failed-units-before-fingerprint.psv" || return $?
|
||||
btrfs subvolume show /.snapshots > "$record/snapshots-subvolume-before-preview.txt" 2>&1
|
||||
rc=$?; [ "$rc" -eq 0 ] || { fail '/.snapshots is not a Btrfs subvolume' "$rc"; return $?; }
|
||||
|
||||
rerun_volatile_checks || return $?
|
||||
|
||||
|
|
@ -228,9 +350,7 @@ main() {
|
|||
normalize_transaction "$record/latest-transaction.psv" "$record/latest-normalized-actions.psv" "$record/latest-downgrades.psv"
|
||||
[ ! -s "$record/latest-downgrades.psv" ] || { fail 'latest transaction contains a downgrade' 14; return $?; }
|
||||
|
||||
awk -F'|' '{print $1"|"$2"|"$3"|"$5}' \
|
||||
/tmp/le-phase2-package-decision-20260712.Mn7mIx/inventory.psv | sort \
|
||||
> "$record/approved-normalized-actions.psv"
|
||||
cp "$approved_transaction/normalized-actions.psv" "$record/approved-normalized-actions.psv"
|
||||
rc=$?
|
||||
[ "$rc" -eq 0 ] || { fail 'cannot normalize approved preview' "$rc"; return $?; }
|
||||
diff -u "$record/approved-normalized-actions.psv" "$record/latest-normalized-actions.psv" \
|
||||
|
|
@ -273,11 +393,19 @@ main() {
|
|||
return $?
|
||||
fi
|
||||
|
||||
create_explicit_pre_upgrade_snapshot || return $?
|
||||
|
||||
printf 'At Pacman Y/n, answer no if any package, version, action, count, downgrade status, signature, or repository differs.\n'
|
||||
script -q -e -f -c 'pacman -Syu --needed rootlesskit slirp4netns openai-codex' "$record/pacman-complete.log"
|
||||
rc=$?
|
||||
if [ "$rc" -ne 0 ]; then fail "Pacman returned $rc; stop without ad hoc repair" "$rc"; return $?; fi
|
||||
|
||||
verify_snapshot_subvolume "$snapshot_number" "$description" || return $?
|
||||
snapper list --columns number,type,pre-number,date,user,cleanup,description \
|
||||
> "$record/snapper-snapshots-after-transaction.txt" 2>&1
|
||||
rc=$?; [ "$rc" -eq 0 ] || { fail 'cannot inventory Snapper snapshots after transaction' "$rc"; return $?; }
|
||||
verify_snap_pac_snapshots || return $?
|
||||
|
||||
pacman -Q > "$record/packages-after.txt" 2>&1
|
||||
rc=$?; [ "$rc" -eq 0 ] || { fail 'cannot capture post-transaction package inventory' "$rc"; return $?; }
|
||||
diff -u "$record/packages-before.txt" "$record/packages-after.txt" > "$record/package-inventory.diff" 2>&1
|
||||
|
|
@ -308,6 +436,7 @@ main() {
|
|||
|
||||
printf 'PACKAGE TRANSACTION COMPLETED; REBOOT NOT PERFORMED.\n'
|
||||
printf 'MAINTENANCE_RECORD=%s\n' "$record"
|
||||
printf 'PRE_UPGRADE_SNAPPER_SNAPSHOT=%s\n' "$snapshot_number"
|
||||
printf 'Review all warnings, hooks, services, kernel artifacts, and pacnew/pacsave files.\n'
|
||||
printf 'Reboot and rootless-Docker provisioning remain separate approval gates.\n'
|
||||
return 0
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue