controller v0.240.0: seven defects from the any-tier proof and the first nightly rotation
gates / gates (push) Successful in 13s
gates / gates (push) Successful in 13s
R-486 (P1): removing an app with its backups KEPT keeps its Tier-2 record, so the second-drive restore is no longer refused over an intact mirror. R-484: postgis/pgvector/timescaledb images are Postgres (logical dumps). R-485: the backup card sizes the recovery unit and the mirror(s). R-480: a held update's sentence leaves the card once the hold is lifted. R-477: the update's off-site lookup is one snapshots call, no stats. R-478: a copy older than this install's deploy does not count. R-474: "delete backups" deletes the unit, the mirror(s) and the prefs. Tests and red-proofs per row; evidence in felhom.eu documentation/audits/v0240-2026-09-13/ and nightly-2026-09-13-adventurelog/.
This commit is contained in:
@@ -43,13 +43,13 @@ func tier1At(at time.Time) func(string) ([]RestorePoint, bool) {
|
||||
}
|
||||
}
|
||||
func noTier1(string) ([]RestorePoint, bool) { return []RestorePoint{}, true }
|
||||
func offsiteWith(app string, at time.Time) func(context.Context) (OffsiteInventory, error) {
|
||||
return func(context.Context) (OffsiteInventory, error) {
|
||||
return OffsiteInventory{Apps: []OffsiteInventoryApp{{App: app, LatestAt: at}}}, nil
|
||||
func offsiteWith(app string, at time.Time) func(context.Context) (map[string]time.Time, error) {
|
||||
return func(context.Context) (map[string]time.Time, error) {
|
||||
return map[string]time.Time{app: at}, nil
|
||||
}
|
||||
}
|
||||
func noOffsiteTarget(context.Context) (OffsiteInventory, error) {
|
||||
return OffsiteInventory{}, errNoOffsiteTarget
|
||||
func noOffsiteTarget(context.Context) (map[string]time.Time, error) {
|
||||
return nil, errNoOffsiteTarget
|
||||
}
|
||||
|
||||
func tiersOf(ps []UpdateTierPoint) []int {
|
||||
@@ -67,7 +67,7 @@ func TestR475_G_TierOrderIsSecondDriveThenOwnUnitThenOffsite(t *testing.T) {
|
||||
offsiteCalls := 0
|
||||
m.updateTier2PointFn = tier2At(r475T0.Add(-1 * time.Hour))
|
||||
m.updateTier1PointsFn = tier1At(r475T0.Add(-2 * time.Hour))
|
||||
m.updateOffsiteInvFn = func(ctx context.Context) (OffsiteInventory, error) {
|
||||
m.updateOffsiteTimesFn = func(ctx context.Context) (map[string]time.Time, error) {
|
||||
offsiteCalls++
|
||||
return offsiteWith("gokapi", r475T0.Add(-3*time.Hour))(ctx)
|
||||
}
|
||||
@@ -94,7 +94,7 @@ func TestR475_H_OwnUnitOnly(t *testing.T) {
|
||||
m, _ := r475Manager()
|
||||
m.updateTier2PointFn = noTier2
|
||||
m.updateTier1PointsFn = tier1At(r475T0.Add(-2 * time.Hour))
|
||||
m.updateOffsiteInvFn = noOffsiteTarget
|
||||
m.updateOffsiteTimesFn = noOffsiteTarget
|
||||
p, ok, _ := m.UpdateRestorePoints(context.Background(), "gokapi", nil)
|
||||
if !ok || p.Tier != UpdateTierLocal || !p.At.Equal(r475T0.Add(-2*time.Hour)) {
|
||||
t.Fatalf("got %+v ok=%v", p, ok)
|
||||
@@ -116,7 +116,7 @@ func TestR475_H_OwnUnitOnly(t *testing.T) {
|
||||
func TestR475_I_OffsiteOnly(t *testing.T) {
|
||||
m, _ := r475Manager()
|
||||
m.updateTier2PointFn, m.updateTier1PointsFn = noTier2, noTier1
|
||||
m.updateOffsiteInvFn = offsiteWith("gokapi", r475T0.Add(-5*time.Hour))
|
||||
m.updateOffsiteTimesFn = offsiteWith("gokapi", r475T0.Add(-5*time.Hour))
|
||||
if p, ok, _ := m.UpdateRestorePoints(context.Background(), "gokapi", nil); !ok || p.Tier != UpdateTierOffsite {
|
||||
t.Fatalf("got %+v ok=%v", p, ok)
|
||||
}
|
||||
@@ -130,8 +130,8 @@ func TestR475_I_OffsiteOnly(t *testing.T) {
|
||||
func TestR475_J_OffsiteUnreachableIsAbsentWithAWarn(t *testing.T) {
|
||||
m, buf := r475Manager()
|
||||
m.updateTier2PointFn, m.updateTier1PointsFn = noTier2, noTier1
|
||||
m.updateOffsiteInvFn = func(context.Context) (OffsiteInventory, error) {
|
||||
return OffsiteInventory{}, errors.New("ssh: connect to host: connection timed out")
|
||||
m.updateOffsiteTimesFn = func(context.Context) (map[string]time.Time, error) {
|
||||
return nil, errors.New("ssh: connect to host: connection timed out")
|
||||
}
|
||||
if _, ok, _ := m.UpdateRestorePoints(context.Background(), "gokapi", nil); ok {
|
||||
t.Error("an unreachable off-site copy must count as absent")
|
||||
@@ -142,7 +142,7 @@ func TestR475_J_OffsiteUnreachableIsAbsentWithAWarn(t *testing.T) {
|
||||
|
||||
// Control: a box with NO off-site target is plainly absent — that is not a fault, so no WARN.
|
||||
buf.Reset()
|
||||
m.updateOffsiteInvFn = noOffsiteTarget
|
||||
m.updateOffsiteTimesFn = noOffsiteTarget
|
||||
if _, ok, _ := m.UpdateRestorePoints(context.Background(), "gokapi", nil); ok || strings.Contains(buf.String(), "WARN") {
|
||||
t.Errorf("no off-site target: want absent and silent; ok=%v log=%q", ok, buf.String())
|
||||
}
|
||||
@@ -152,9 +152,9 @@ func TestR475_J_OffsiteUnreachableIsAbsentWithAWarn(t *testing.T) {
|
||||
updateOffsiteCheckTimeout = 50 * time.Millisecond
|
||||
defer func() { updateOffsiteCheckTimeout = old }()
|
||||
buf.Reset()
|
||||
m.updateOffsiteInvFn = func(ctx context.Context) (OffsiteInventory, error) {
|
||||
m.updateOffsiteTimesFn = func(ctx context.Context) (map[string]time.Time, error) {
|
||||
<-ctx.Done()
|
||||
return OffsiteInventory{}, ctx.Err()
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
start := time.Now()
|
||||
_, ok, _ := m.UpdateRestorePoints(context.Background(), "gokapi", nil)
|
||||
|
||||
Reference in New Issue
Block a user