v0.228.0 — the off-site check reads the data; the debug page stops lying (R-399 + R-400)
gates / gates (push) Successful in 12s
gates / gates (push) Successful in 12s
R-399: monitoring.integrity.read_data_subset defaults to 100%. A pack damaged without changing its size made plain `restic check` report "no errors were found" on demo-hp 2026-08-30; every read-data form caught it. Cost on that 134 MB store: 35.0s structure vs 39.2s at 100%. "off" (any case) is the off token; empty means not-configured, therefore the default; a malformed value falls back to the DEFAULT, never to structure. A completed check over 5 minutes logs a WARN naming the duration, the depth and R-401 — operator log only, no hub event, no depth change. The depth is now recorded with the verdict (LastIntegrityDepth; empty = NOT RECORDED, never "structure"). R-400: 24 debug-page references, 17 dispatched, 7 dead — three of which fetched on page LOAD, so those panels were permanently blank. backup/crossdrive implemented; backup/infra, hub/infra-push, dr/infra-status, storage/watchdog-status and both storage/simulate-* deleted with their panels and JavaScript. scripts/debug_route_gate.py fails in both directions and is registered after the seven were resolved. 18 referenced, 18 dispatched, none orphaned. Corrections: the dead-field warning in report/types.go said the controller runs no integrity check and the notifiers are called from nowhere — both false since v0.227.0. controller.yaml.example gains its missing integrity: block. integrityCheckTimeout's "ships OFF" comment rewritten.
This commit is contained in:
@@ -157,21 +157,17 @@ func TestR359_TimeoutIsNotDamage(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestR359_StructureCheckPassesNoReadDataFlag(t *testing.T) {
|
||||
m, cap := newIntegrityManager(t, okRepo(nil))
|
||||
m.CheckOffboxIntegrity(context.Background())
|
||||
|
||||
argv := cap.checkArgv()
|
||||
if argv == nil {
|
||||
t.Fatal("no check ran")
|
||||
}
|
||||
for _, a := range argv {
|
||||
if strings.HasPrefix(a, "--read-data") {
|
||||
t.Fatalf("the DEFAULT check downloaded pack data (%q) — that is a bandwidth cost nobody "+
|
||||
"chose, and R-399 exists precisely so it is not chosen here", a)
|
||||
}
|
||||
}
|
||||
}
|
||||
// TestR359_StructureCheckPassesNoReadDataFlag was DELETED on 2026-08-31, superseded by R-399.
|
||||
//
|
||||
// It asserted that an unconfigured box passes NO --read-data flag. That was the correct contract on
|
||||
// 2026-08-30, when nothing had measured the cost of a deeper check. The next day a size-preserving
|
||||
// pack corruption was shown to PASS that structure-only check on real hardware, and Viktor ruled the
|
||||
// default to full depth. The test is not weakened, it is inverted: its replacement is
|
||||
// TestR399_AbsentConfigRunsFullDepth in r399_depth_test.go, and the off token it left room for is
|
||||
// pinned by TestR399_OffTokenRunsStructureOnly.
|
||||
//
|
||||
// Recorded here rather than removed silently, so a later reader does not re-derive the old ruling
|
||||
// from its absence.
|
||||
|
||||
func TestR359_ReadDataSubsetIsPassedWhenConfigured(t *testing.T) {
|
||||
m, cap := newIntegrityManager(t, okRepo(nil))
|
||||
@@ -192,25 +188,12 @@ func TestR359_ReadDataSubsetIsPassedWhenConfigured(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestR359_MalformedReadDataSubsetIsTreatedAsOff(t *testing.T) {
|
||||
m, cap := newIntegrityManager(t, okRepo(nil))
|
||||
m.cfg.Monitoring.Integrity.ReadDataSubset = "banana"
|
||||
res := m.CheckOffboxIntegrity(context.Background())
|
||||
|
||||
for _, a := range cap.checkArgv() {
|
||||
if strings.HasPrefix(a, "--read-data") {
|
||||
t.Fatalf("a malformed value was handed to restic (%q) — restic rejects it and the WHOLE "+
|
||||
"check fails, so one typo silently stops the store being verified at all", a)
|
||||
}
|
||||
}
|
||||
if res.ReadDataSubset != "" {
|
||||
t.Errorf("a refused value was still recorded as the depth: %q", res.ReadDataSubset)
|
||||
}
|
||||
if !strings.Contains(cap.logBuf.String(), "WARN") {
|
||||
t.Error("a refused config value must say so — silence makes a typo indistinguishable from a " +
|
||||
"deliberate structure-only setting")
|
||||
}
|
||||
}
|
||||
// TestR359_MalformedReadDataSubsetIsTreatedAsOff was DELETED on 2026-08-31, superseded by R-399.
|
||||
//
|
||||
// Its NAME was the defect. Treating a typo as "off" downgrades the check silently, which is R-357's
|
||||
// shape — a guard that opens quietly. The half of it that still holds (a malformed value never
|
||||
// reaches restic, and it WARNs) is asserted by TestR399_MalformedFallsBackToTheDefault, which also
|
||||
// pins the new direction: the fallback is the DEFAULT depth, never structure.
|
||||
|
||||
func TestR359_MessageNeverCarriesResticOutputOrCredentials(t *testing.T) {
|
||||
// R-379: 615 bytes of raw database text reached a customer once. And offboxBaseArgs builds the repo
|
||||
|
||||
Reference in New Issue
Block a user