From 1f5046f47ece60416f4464e2745fb2899ac04dd1 Mon Sep 17 00:00:00 2001 From: mesh-tools Date: Wed, 21 Oct 2026 07:59:55 +0000 Subject: [PATCH] Declare the weekly upgrade timer with its health, and serve pacman_upgrade_health (issue 1574, ADR 0477) --- modules/pacman/cmd/pacman-tools/acts.go | 7 +++- modules/pacman/cmd/pacman-tools/main.go | 7 ++++ modules/pacman/module.json | 44 +++++++++++++++++++++++-- 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/modules/pacman/cmd/pacman-tools/acts.go b/modules/pacman/cmd/pacman-tools/acts.go index a0b4e3c3..146d6d5e 100644 --- a/modules/pacman/cmd/pacman-tools/acts.go +++ b/modules/pacman/cmd/pacman-tools/acts.go @@ -55,6 +55,11 @@ func (m *Machine) journal(unit string, n int) []string { return lines(out) } +// UpgradeArgs is the full system upgrade, in one place: pacman_upgrade starts pacman with these arguments as +// a transient unit, and the weekly upgrade's service (files/mesh-pacman-weekly-upgrade.service) runs +// pacman with the same ones; a test holds the two together (issue 1574, ADR 0477 clause 5). +var UpgradeArgs = []string{"-Syu", "--noconfirm"} + // Upgrade starts a full system upgrade as a transient unit and answers at once, with the // distribution's news since the last upgrade; or, given a unit it started, answers how it went. func (m *Machine) Upgrade(unit string, n int) (map[string]any, error) { @@ -62,7 +67,7 @@ func (m *Machine) Upgrade(unit string, n int) (map[string]any, error) { return m.UpgradeStatus(unit, n) } news := m.News("") - started, _, err := m.transaction("upgrade", false, "-Syu", "--noconfirm") + started, _, err := m.transaction("upgrade", false, UpgradeArgs...) if err != nil { return nil, err } diff --git a/modules/pacman/cmd/pacman-tools/main.go b/modules/pacman/cmd/pacman-tools/main.go index 06ead071..c4d7136a 100644 --- a/modules/pacman/cmd/pacman-tools/main.go +++ b/modules/pacman/cmd/pacman-tools/main.go @@ -152,6 +152,13 @@ func tools(m *Machine) []stdio.Tool { return m.Upgrade(unit, n) }, }, + { + Name: "pacman_upgrade_health", + Description: "Whether the weekly full upgrade (Saturday 04:00 local time, ADR 0477) last passed, as the module's health check: unhealthy " + + "with the exit and the last error of the journal when it failed, healthy while it runs, after it passed and before it ever ran. (r)", + Input: schema(map[string]any{}), + Run: func(map[string]any) (any, error) { return m.UpgradeHealth(), nil }, + }, { Name: "pacman_orphans", Description: "Packages installed as dependencies that nothing requires any more (pacman -Qdt), with versions.", diff --git a/modules/pacman/module.json b/modules/pacman/module.json index 746c8c5a..ca1a81a3 100644 --- a/modules/pacman/module.json +++ b/modules/pacman/module.json @@ -19,6 +19,7 @@ "pacman_files", "pacman_updates", "pacman_upgrade", + "pacman_upgrade_health", "pacman_orphans", "pacman_remove_orphans", "pacman_cache", @@ -63,14 +64,53 @@ "type": "service", "unit": "reflector.timer", "state": "running", - "boot": "enabled" + "boot": "enabled", + "health": { + "kind": "unit" + } }, { "id": "cache-cleaning", "type": "service", "unit": "paccache.timer", "state": "running", - "boot": "enabled" + "boot": "enabled", + "health": { + "kind": "unit" + } + }, + { + "id": "upgrade-service", + "type": "file", + "path": "/etc/systemd/system/mesh-pacman-weekly-upgrade.service", + "mode": "0644", + "template": "files/mesh-pacman-weekly-upgrade.service" + }, + { + "id": "upgrade-timer", + "type": "file", + "path": "/etc/systemd/system/mesh-pacman-weekly-upgrade.timer", + "mode": "0644", + "template": "files/mesh-pacman-weekly-upgrade.timer" + }, + { + "id": "weekly-upgrade", + "type": "service", + "unit": "mesh-pacman-weekly-upgrade.timer", + "state": "running", + "boot": "enabled", + "restart-on": [ + "upgrade-service", + "upgrade-timer" + ], + "health": { + "kind": "tool", + "tool": "pacman_upgrade_health", + "interval": "60s", + "timeout": "20s", + "looks": 2, + "grace": "60s" + } } ], "build": {