Record a backup that cannot start as the module's last night (issue 1067)

This commit is contained in:
2026-10-17 22:42:21 +02:00
parent f076cadece
commit 914099774e
2 changed files with 36 additions and 1 deletions
+9 -1
View File
@@ -26,8 +26,16 @@ import (
const HandTag = "hand"
// BackUpNow is a backup asked for now, of every module or one: as a night, each restore point tagged HandTag.
//
// One that cannot start (the lock cannot be taken, the repository cannot be opened, the module declares nothing)
// is recorded as that module's last night, failed and with the reason: the mesh asking before a delivery reads
// it from backed-up, and would otherwise read silence (novox/hq issue 1067).
func (b *Backups) BackUpNow(ctx context.Context, only string) (map[string]Night, error) {
return b.backUp(ctx, only, true)
out, err := b.backUp(ctx, only, true)
if err != nil && only != "" {
b.record(only, Night{OK: false, At: b.Now().UTC(), Error: err.Error()})
}
return out, err
}
// isHand is whether a restore point was taken by hand.
@@ -324,3 +324,30 @@ func TestAThinningThatFailsAfterLettingGoSaysWhatItLetGo(t *testing.T) {
t.Errorf("said %q, want it to say aaaaaaaa was let go", why)
}
}
// A backup asked for now that cannot start leaves its failure as the module's last night, so the mesh asking
// for it before a delivery reads the backup's own words from backed-up, not silence (novox/hq issue 1067).
func TestABackupAskedForNowThatCannotStartIsRecordedAsThatModulesLastNight(t *testing.T) {
h := &handRestic{}
asked := time.Date(2026, 10, 17, 22, 50, 0, 0, time.UTC)
b := &Backups{Where: placed(t, "# pg\npath /\n"), Run: h.run, Now: func() time.Time { return asked }, Say: quiet}
must(t, os.Mkdir(filepath.Join(b.Where.State, nightLock), 0o700)) // a directory cannot be opened read-write: the lock cannot be taken
if _, err := b.BackUpNow(context.Background(), "pg"); err == nil {
t.Fatal("a backup started without its lock")
}
night, ok := b.Nights()["pg"]
if !ok || night.OK || night.Error == "" || !night.At.Equal(asked) {
t.Fatalf("pg's last night is %+v, want a failure at %s with the reason", night, asked)
}
if !strings.Contains(night.Error, nightLock) {
t.Errorf("the reason is %q, want it to name the lock", night.Error)
}
// Asked of a module that declares nothing here, once the lock can be taken: recorded too, with the reason.
must(t, os.Remove(filepath.Join(b.Where.State, nightLock)))
if _, err := b.BackUpNow(context.Background(), "mail"); err == nil {
t.Fatal("a backup of a module that declares nothing started")
}
if n := b.Nights()["mail"]; n.OK || !strings.Contains(n.Error, "declares nothing") {
t.Errorf("mail's last night is %+v, want a failure saying it declares nothing", n)
}
}