mesh/merge-gate head 3ad9d42be923 merged into trunk 083515da1be7 — pass: builds pacman → ace, g14, novox, shanks; no bus step; every machine composes with …
mesh/repo-check head 3ad9d42be923 merged into trunk 083515da1be7 — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
mesh/delivery delivered
mesh/check failed: unit: i3/cmd/i3-tools.TestTheMainFileAndEveryModulesContributionLoadTogether: manifest_test.go:247: expected the launcher, clipboar…
116 lines
5.1 KiB
Go
116 lines
5.1 KiB
Go
package main
|
|
|
|
// A run read from pacman's log, or a run whose transaction ended, never holds a new run back (issue 1841, the
|
|
// refusal of 2026-10-22 22:00 on the control-node: "run 20220123T194900Z is still going (its last step is finished): one
|
|
// run at a time"); and pacman_upgrades reads one run whole at an address that names the machine.
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// installLogHead is the first twelve lines of the control-node's /var/log/pacman.log, captured 2026-10-22 22:10
|
|
// with node-reads.read: the install's `pacman -Syu` with nothing to do, which became run 20220123T194900Z, its
|
|
// last step finished at the moment it began (pacman_upgrades there, 2026-10-22: from log, outcome finished,
|
|
// began 2022-01-23T19:49:00Z, ended 2022-01-23T20:49:00+01:00).
|
|
const installLogHead = "[2022-01-23T20:49:00+0100] [PACMAN] Running 'pacman -Syu'\n" +
|
|
"[2022-01-23T20:49:00+0100] [PACMAN] synchronizing package lists\n" +
|
|
"[2022-01-23T20:49:00+0100] [PACMAN] starting full system upgrade\n" +
|
|
"[2022-01-23T20:50:45+0100] [PACMAN] Running 'pacman -S nano'\n" +
|
|
"[2022-01-23T20:50:49+0100] [ALPM] transaction started\n" +
|
|
"[2022-01-23T20:50:50+0100] [ALPM] installed nano (6.0-1)\n" +
|
|
"[2022-01-23T20:50:50+0100] [ALPM] transaction completed\n" +
|
|
"[2022-01-23T20:50:50+0100] [ALPM] running '30-systemd-update.hook'...\n" +
|
|
"[2022-01-23T20:55:31+0100] [PACMAN] Running 'pacman -S docker'\n" +
|
|
"[2022-01-23T20:55:36+0100] [ALPM] transaction started\n" +
|
|
"[2022-01-23T20:55:36+0100] [ALPM] installed bridge-utils (1.7.1-1)\n" +
|
|
"[2022-01-23T20:55:36+0100] [ALPM] installed libtool (2.4.6+42+gb88cebd5-16)\n"
|
|
|
|
// 1841/AC20: given the run read from the control-node's log, when an upgrade is asked, then it starts.
|
|
func TestARunReadFromTheLogIsHistoryAndNeverHoldsAnAsk(t *testing.T) {
|
|
k := newKeeperBed(t)
|
|
past := PastUpgrades("node-a", installLogHead)
|
|
if len(past) != 1 || past[0].Run != "20220123T194900Z" || steps(past[0]) != "started finished" || past[0].From != FromLog {
|
|
t.Fatalf("the captured log reads as the record the control-node holds: %+v", past)
|
|
}
|
|
if _, err := k.runs.Create(NodeKey("node-a", past[0].Run), past[0]); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := k.u.StartUpgrade(FromAsk, "", "a1"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var r Run
|
|
k.runs.value("node-a.20261004T120000Z", &r)
|
|
if lastStep(r) == StepRefused || len(k.root.started) != 1 {
|
|
t.Errorf("a run read from the log held the ask back: %s %+v", steps(r), r.Steps)
|
|
}
|
|
}
|
|
|
|
// 1841/AC21: given a run the mesh ran whose last step is finished, failed or refused (no after-upgrade check
|
|
// yet, no end time of the run itself), when another run would start, then the earlier one does not hold it;
|
|
// a run whose transaction may still go on (no step, recorded, checked before, started) does.
|
|
func TestARunWhoseTransactionEndedNeverHoldsTheNext(t *testing.T) {
|
|
at := time.Date(2026, 10, 22, 20, 0, 0, 0, time.UTC)
|
|
for last, holds := range map[string]bool{
|
|
StepFinished: false, StepFailed: false, StepRefused: false, StepCheckedAfter: false,
|
|
"": true, StepRecorded: true, StepCheckedBefore: true, StepStarted: true,
|
|
} {
|
|
r := Run{Node: "node-a", Run: "20261022T200000Z", Kind: RunUpgrade, From: FromAsk}
|
|
if last != "" {
|
|
r.Steps = []Step{{Step: last, At: at}}
|
|
}
|
|
if got := holdsTheDatabase(r); got != holds {
|
|
t.Errorf("last step %q: holds %v, want %v", last, got, holds)
|
|
}
|
|
r.From = FromLog
|
|
if holdsTheDatabase(r) {
|
|
t.Errorf("a run read from the log, last step %q, holds the next", last)
|
|
}
|
|
}
|
|
|
|
k := newKeeperBed(t)
|
|
done := Run{Node: "node-a", Run: "20261004T110000Z", Kind: RunUpgrade, From: FromAsk,
|
|
Steps: []Step{{Step: StepStarted, At: k.now}, {Step: StepFinished, At: k.now}}}
|
|
if _, err := k.runs.Create(NodeKey("node-a", done.Run), done); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := k.u.StartUpgrade(FromAsk, "", "a1"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var r Run
|
|
k.runs.value("node-a.20261004T120000Z", &r)
|
|
if lastStep(r) == StepRefused {
|
|
t.Errorf("a finished run held the next: %+v", r.Steps)
|
|
}
|
|
}
|
|
|
|
// 1841/AC22: given an address that names the machine (the console takes `node` out of the arguments), when
|
|
// pacman_upgrades is asked for one run, then that machine's run is answered whole.
|
|
func TestPacmanUpgradesReadsOneRunOfThisNodeWithoutANode(t *testing.T) {
|
|
runs := newBucket()
|
|
past := PastUpgrades("node-a", installLogHead)[0]
|
|
if _, err := runs.Create(NodeKey("node-a", past.Run), past); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
savedState, savedNode := upgradeState, thisNode
|
|
upgradeState = func(string) Bucket { return runs }
|
|
thisNode = func() string { return "node-a" }
|
|
defer func() { upgradeState, thisNode = savedState, savedNode }()
|
|
for _, tool := range tools(machine(nil, 1000)) {
|
|
if tool.Name != "pacman_upgrades" {
|
|
continue
|
|
}
|
|
out, err := tool.Run(map[string]any{"run": past.Run})
|
|
if err != nil {
|
|
t.Fatalf("one run without a node: %v", err)
|
|
}
|
|
whole := out.(map[string]any)["run"].(Run)
|
|
if whole.Node != "node-a" || steps(whole) != "started finished" || !strings.Contains(whole.Steps[1].Why, "no transaction") {
|
|
t.Errorf("one run whole: %+v", whole)
|
|
}
|
|
return
|
|
}
|
|
t.Fatal("no pacman_upgrades")
|
|
}
|