9bdb4dae8f
gates / gates (push) Successful in 13s
Space preflight before anything is created (uncompressed size from the vzdump log / PBS snapshot, x1.2 + 5 GiB, thin metadata, off the tested guest's pool when another storage is eligible, unknown refuses, reported as a non-pass result). Failed scratch teardown and the stale-lock sweep retried every 10 min (the sweep under the heavy-op gate). A thin pool crossing 90% requests an immediate host report. Six red-proofs. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
61 lines
2.1 KiB
Go
61 lines
2.1 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"log/slog"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"gitea.dooplex.hu/admin/felhom-agent/internal/backup"
|
|
"gitea.dooplex.hu/admin/felhom-agent/internal/hub"
|
|
"gitea.dooplex.hu/admin/felhom-agent/internal/reconcile"
|
|
)
|
|
|
|
// R-672 / R-673 (v0.133.0): one janitor pass, driven with fakes.
|
|
|
|
func quiet() *slog.Logger { return slog.New(slog.NewTextHandler(io.Discard, nil)) }
|
|
|
|
// A scratch the engine gave up on reaches the hub as a failed restore-test record naming it — the
|
|
// existing operator path (restore_test_failed). Never a pass.
|
|
func TestJanitor_GaveUpIsReportedAsAFailure(t *testing.T) {
|
|
var got []hub.RestoreTest
|
|
janitorPass(context.Background(), janitorDeps{
|
|
retryScratch: func(context.Context) reconcile.ScratchRetryResult {
|
|
return reconcile.ScratchRetryResult{Examined: 1, Failed: 1, GaveUp: []int{990000}}
|
|
},
|
|
heavy: &backup.InFlight{}, record: func(r hub.RestoreTest) { got = append(got, r) },
|
|
now: time.Now, logger: quiet(),
|
|
})
|
|
if len(got) != 1 || got[0].Pass || got[0].ScratchVMID != 990000 || !strings.Contains(got[0].Error, "990000") {
|
|
t.Fatalf("records = %+v — want one FAILED record naming scratch 990000", got)
|
|
}
|
|
}
|
|
|
|
// R-673: the stale-lock sweep runs only while holding the one-heavy-operation gate, so no agent backup can
|
|
// start between its "no vzdump running" check and its unlock.
|
|
//
|
|
// COMPANION RED-PROOF (REPORT): drop the TryAcquire → "the sweep ran while a backup held the gate".
|
|
func TestJanitor_StaleLockSweepWaitsForTheHeavyGate(t *testing.T) {
|
|
heavy := &backup.InFlight{}
|
|
swept := 0
|
|
d := janitorDeps{staleLocks: func(context.Context) { swept++ }, heavy: heavy, now: time.Now, logger: quiet()}
|
|
release, _, ok := heavy.TryAcquire("backup")
|
|
if !ok {
|
|
t.Fatal("setup")
|
|
}
|
|
janitorPass(context.Background(), d)
|
|
if swept != 0 {
|
|
t.Fatal("the sweep ran while a backup held the gate")
|
|
}
|
|
release()
|
|
janitorPass(context.Background(), d)
|
|
if swept != 1 {
|
|
t.Fatalf("swept %d times with the gate free — want 1", swept)
|
|
}
|
|
if _, _, ok := heavy.TryAcquire("after"); !ok {
|
|
t.Fatal("the sweep did not release the gate")
|
|
}
|
|
}
|