From 9baa0a10af55411bcc4721224fd74a35639086a5 Mon Sep 17 00:00:00 2001 From: Lemon-miaow Date: Thu, 24 Sep 2026 23:10:28 +0800 Subject: [PATCH] =?UTF-8?q?fix(registrygate):=20=E5=90=AF=E5=8A=A8?= =?UTF-8?q?=E5=90=8E=E5=85=88=E7=AD=89=E5=86=99=E5=85=A5=E9=9D=99=E9=BB=98?= =?UTF-8?q?=E5=86=8D=E5=BC=80=E6=94=BE=20GC=20=E7=AA=97=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/registrygate/gate.go | 4 ++++ internal/registrygate/maint_test.go | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/internal/registrygate/gate.go b/internal/registrygate/gate.go index d0ea51b..71d526c 100644 --- a/internal/registrygate/gate.go +++ b/internal/registrygate/gate.go @@ -95,6 +95,10 @@ func New(upstream *url.URL, tokens map[string]string, log *slog.Logger) *Gate { g.health = &http.Client{Timeout: 3 * time.Second} g.maint.now = time.Now g.maint.quiet = DefaultQuiet + // A gate that just started cannot tell whether a push was mid-way through the + // previous one, and the installer pushes right after the registry rolls out: + // count the start as a write, so the first window waits for quiet too. + g.maint.lastWrite = g.maint.now() return g } diff --git a/internal/registrygate/maint_test.go b/internal/registrygate/maint_test.go index 83cfa9b..18b4c5d 100644 --- a/internal/registrygate/maint_test.go +++ b/internal/registrygate/maint_test.go @@ -42,6 +42,7 @@ func newMaintGate(t *testing.T) (*Gate, *httptest.Server, *httptest.Server, *ups }, nil) clk := &clock{t: time.Date(2026, 9, 24, 3, 0, 0, 0, time.UTC)} g.maint.now = clk.now + g.maint.lastWrite = time.Time{} gs := httptest.NewServer(g) t.Cleanup(gs.Close) ms := httptest.NewServer(g.MaintHandler()) @@ -276,3 +277,21 @@ func TestManifestIndexListsUntaggedRevisions(t *testing.T) { } } } + +// A gate that just started grants no window before a quiet period has passed: +// the installer pushes right after the registry rolls out. +func TestReadOnlyWindowWaitsAfterStart(t *testing.T) { + up := httptest.NewServer(http.NotFoundHandler()) + t.Cleanup(up.Close) + target, _ := url.Parse(up.URL) + g := New(target, nil, nil) + ms := httptest.NewServer(g.MaintHandler()) + t.Cleanup(ms.Close) + if code := post(t, ms, "/readonly?lease=60"); code != http.StatusConflict { + t.Fatalf("read-only window right after start = %d, want 409", code) + } + g.SetQuiet(0) + if code := post(t, ms, "/readonly?lease=60"); code != http.StatusOK { + t.Fatalf("read-only window once quiet = %d, want 200", code) + } +}