R-977: a new FileBrowser run is given ~20 s to answer in the same tick
gates / gates (push) Successful in 44s
gates / gates (push) Successful in 44s
rc.1 on 9202: the tick that brought FileBrowser back probed it while it was starting; the default login stayed open for one 5-minute round. TestFileBrowserAdmin_R977_NewRunIsWaitedForInTheSameTick, red-proved. Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
+4
-2
@@ -8,8 +8,10 @@
|
|||||||
(the container's StartedAt), not once per box. Measured first on scratch 9202 with 0.324.0: FileBrowser and its data
|
(the container's StartedAt), not once per box. Measured first on scratch 9202 with 0.324.0: FileBrowser and its data
|
||||||
volume removed, the box brought it back on its own — the default login answered 200 while the box kept its old
|
volume removed, the box brought it back on its own — the default login answered 200 while the box kept its old
|
||||||
„generated" record. Now such a run is reset at its first tick (a generated password, verified both ways, stored
|
„generated" record. Now such a run is reset at its first tick (a generated password, verified both ways, stored
|
||||||
encrypted); a run on any other password — the household's own — changes nothing. `TestFileBrowserAdmin_R977_*`,
|
encrypted); a run on any other password — the household's own — changes nothing. A NEW run is given ~20 s to answer
|
||||||
red-proved (the old once-per-box return keeps the default). The code comment „before a claim there is no tunnel"
|
in the same tick (rc.1 on 9202: the tick that brought FileBrowser back probed it while it was starting, and the
|
||||||
|
default stayed open for one whole 5-minute round). `TestFileBrowserAdmin_R977_*` (2), each red-proved (the old
|
||||||
|
once-per-box return keeps the default; one try per tick leaves it open). The code comment „before a claim there is no tunnel"
|
||||||
was false (the claim is reached through the tunnel) and is corrected; the tunnel itself is NOT changed — R-1120.
|
was false (the claim is reached through the tunnel) and is corrected; the tunnel itself is NOT changed — R-1120.
|
||||||
- **R-1116** — the decoy suite is a gate of the full run (below, the helper's note).
|
- **R-1116** — the decoy suite is a gate of the full run (below, the helper's note).
|
||||||
|
|
||||||
|
|||||||
@@ -92,6 +92,15 @@ func (m *Manager) fbStartedAt() string {
|
|||||||
return strings.TrimSpace(string(out))
|
return strings.TrimSpace(string(out))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fbProbeWait is the pause between two probes of a new FileBrowser run: 2 s in production, 1 ms against a test
|
||||||
|
// FileBrowser.
|
||||||
|
func (m *Manager) fbProbeWait() time.Duration {
|
||||||
|
if m.fbHTTP != nil || m.fbBaseURL != "" {
|
||||||
|
return time.Millisecond
|
||||||
|
}
|
||||||
|
return 2 * time.Second
|
||||||
|
}
|
||||||
|
|
||||||
// EnsureFileBrowserAdminPassword probes FileBrowser's default login once per FileBrowser run (R-977) and replaces it
|
// EnsureFileBrowserAdminPassword probes FileBrowser's default login once per FileBrowser run (R-977) and replaces it
|
||||||
// when it works. Safe to call every tick: a run already probed returns at once. Returns an error only for logging.
|
// when it works. Safe to call every tick: a run already probed returns at once. Returns an error only for logging.
|
||||||
func (m *Manager) EnsureFileBrowserAdminPassword() error {
|
func (m *Manager) EnsureFileBrowserAdminPassword() error {
|
||||||
@@ -110,7 +119,26 @@ func (m *Manager) EnsureFileBrowserAdminPassword() error {
|
|||||||
}
|
}
|
||||||
now := time.Now().UTC().Format(time.RFC3339)
|
now := time.Now().UTC().Format(time.RFC3339)
|
||||||
|
|
||||||
token, code, err := fbLogin(do, base, "admin")
|
// A NEW run is usually seconds old — the same tick that brought FileBrowser back calls this right after — so it is
|
||||||
|
// given ~20 s to answer before the probe gives up until the next tick (measured on 9202 2026-10-17: without the
|
||||||
|
// wait, a re-created FileBrowser kept its default login for one whole base-stack round, 5 minutes). A run already
|
||||||
|
// probed, or one that cannot be told, gets one try, as before.
|
||||||
|
tries := 1
|
||||||
|
if run != "" && run != m.fbProbedRun {
|
||||||
|
tries = 10
|
||||||
|
}
|
||||||
|
var token string
|
||||||
|
var code int
|
||||||
|
var err error
|
||||||
|
for i := 0; i < tries; i++ {
|
||||||
|
token, code, err = fbLogin(do, base, "admin")
|
||||||
|
if err == nil && (code == http.StatusOK || code == http.StatusUnauthorized || code == http.StatusForbidden) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if i+1 < tries {
|
||||||
|
time.Sleep(m.fbProbeWait())
|
||||||
|
}
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("filebrowser admin probe: %w (will retry)", err)
|
return fmt.Errorf("filebrowser admin probe: %w (will retry)", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -210,3 +210,37 @@ func TestFileBrowserAdmin_R977_DefaultBackOnANewRunIsReplaced(t *testing.T) {
|
|||||||
t.Fatalf("the recorded decision moved on a run that is not on the default: state=%q", s)
|
t.Fatalf("the recorded decision moved on a run that is not on the default: state=%q", s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// R-977, measured live: the tick that brings FileBrowser back probes it while it is still starting, and without a wait
|
||||||
|
// the default login stayed open until the next round (5 minutes on 9202). A NEW run is given a short wait.
|
||||||
|
// RED-PROOF (2026-10-17): with one try per tick this fails at „the default stayed open".
|
||||||
|
func TestFileBrowserAdmin_R977_NewRunIsWaitedForInTheSameTick(t *testing.T) {
|
||||||
|
fb := &fakeFB{password: "admin"}
|
||||||
|
starting := 3 // the first three answers: FileBrowser not ready yet
|
||||||
|
inner := fb.handler()
|
||||||
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
fb.mu.Lock()
|
||||||
|
notReady := starting > 0
|
||||||
|
if notReady {
|
||||||
|
starting--
|
||||||
|
}
|
||||||
|
fb.mu.Unlock()
|
||||||
|
if notReady {
|
||||||
|
w.WriteHeader(http.StatusBadGateway)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
inner.ServeHTTP(w, r)
|
||||||
|
}))
|
||||||
|
defer srv.Close()
|
||||||
|
m, st, _ := fbManager(t, srv.URL)
|
||||||
|
m.fbStarted = func() string { return "2026-10-17T09:24:35Z" }
|
||||||
|
if err := m.EnsureFileBrowserAdminPassword(); err != nil {
|
||||||
|
t.Fatalf("one tick: %v", err)
|
||||||
|
}
|
||||||
|
if _, c, _ := fbLogin(srv.Client().Do, srv.URL, "admin"); c == http.StatusOK {
|
||||||
|
t.Fatal("the default stayed open after the tick that started FileBrowser")
|
||||||
|
}
|
||||||
|
if s, _, _ := st.GetFileBrowserAdmin(); s != settings.FileBrowserAdminGenerated {
|
||||||
|
t.Fatalf("state %q, want generated", s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user