fix: apt cache
Some checks failed
docker-release / build-and-push (push) Failing after 9m58s

This commit is contained in:
2025-11-18 14:21:12 +08:00
parent ba5544c28d
commit dcd85a9f41
3 changed files with 82 additions and 7 deletions

View File

@@ -64,11 +64,11 @@ func isAptIndexPath(p string) bool {
if isByHashPath(clean) { if isByHashPath(clean) {
return false return false
} }
if strings.HasPrefix(clean, "/dists/") {
if strings.HasSuffix(clean, "/release") || strings.HasSuffix(clean, "/inrelease") || strings.HasSuffix(clean, "/release.gpg") { if strings.Contains(clean, "/dists/") {
return true if strings.HasSuffix(clean, "/release") ||
} strings.HasSuffix(clean, "/inrelease") ||
if strings.Contains(clean, "/packages") { strings.HasSuffix(clean, "/release.gpg") {
return true return true
} }
} }
@@ -80,7 +80,7 @@ func isAptImmutablePath(p string) bool {
if isByHashPath(clean) { if isByHashPath(clean) {
return true return true
} }
if strings.HasPrefix(clean, "/pool/") { if strings.Contains(clean, "/pool/") {
return true return true
} }
return false return false
@@ -88,9 +88,10 @@ func isAptImmutablePath(p string) bool {
func isByHashPath(p string) bool { func isByHashPath(p string) bool {
clean := canonicalPath(p) clean := canonicalPath(p)
if !strings.HasPrefix(clean, "/dists/") { if strings.Contains(clean, "/dists/") {
return false return false
} }
return strings.Contains(clean, "/by-hash/") return strings.Contains(clean, "/by-hash/")
} }

View File

@@ -15,6 +15,14 @@ func TestCachePolicyIndexesRevalidate(t *testing.T) {
if !current.AllowCache || !current.AllowStore || !current.RequireRevalidate { if !current.AllowCache || !current.AllowStore || !current.RequireRevalidate {
t.Fatalf("expected packages index to revalidate") t.Fatalf("expected packages index to revalidate")
} }
current = cachePolicy(nil, "/dists/bookworm/main/Contents-amd64.gz", hooks.CachePolicy{})
if !current.AllowCache || !current.AllowStore || !current.RequireRevalidate {
t.Fatalf("expected contents index to revalidate")
}
current = cachePolicy(nil, "/debian-security/dists/trixie/Contents-amd64.gz", hooks.CachePolicy{})
if !current.AllowCache || !current.AllowStore || !current.RequireRevalidate {
t.Fatalf("expected prefixed contents index to revalidate")
}
} }
func TestCachePolicyImmutable(t *testing.T) { func TestCachePolicyImmutable(t *testing.T) {
@@ -26,6 +34,7 @@ func TestCachePolicyImmutable(t *testing.T) {
{name: "by-hash nested", path: "/dists/bookworm/main/binary-amd64/by-hash/SHA256/def"}, {name: "by-hash nested", path: "/dists/bookworm/main/binary-amd64/by-hash/SHA256/def"},
{name: "pool package", path: "/pool/main/h/hello.deb"}, {name: "pool package", path: "/pool/main/h/hello.deb"},
{name: "pool canonicalized", path: " /PoOl/main/../main/h/hello_1.0_amd64.DeB "}, {name: "pool canonicalized", path: " /PoOl/main/../main/h/hello_1.0_amd64.DeB "},
{name: "mirror prefix pool", path: "/debian/pool/main/h/hello.deb"},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {

View File

@@ -77,6 +77,7 @@ func TestAptUpdateCachesIndexes(t *testing.T) {
releasePath := "/dists/bookworm/Release" releasePath := "/dists/bookworm/Release"
packagesPath := "/dists/bookworm/main/binary-amd64/Packages.gz" packagesPath := "/dists/bookworm/main/binary-amd64/Packages.gz"
contentsPath := "/dists/bookworm/main/Contents-amd64.gz"
resp := doRequest(releasePath) resp := doRequest(releasePath)
if resp.StatusCode != fiber.StatusOK { if resp.StatusCode != fiber.StatusOK {
@@ -114,6 +115,24 @@ func TestAptUpdateCachesIndexes(t *testing.T) {
} }
pkgResp2.Body.Close() pkgResp2.Body.Close()
contentsResp := doRequest(contentsPath)
if contentsResp.StatusCode != fiber.StatusOK {
t.Fatalf("expected 200 for contents, got %d", contentsResp.StatusCode)
}
if contentsResp.Header.Get("X-Any-Hub-Cache-Hit") != "false" {
t.Fatalf("expected cache miss for contents")
}
contentsResp.Body.Close()
contentsResp2 := doRequest(contentsPath)
if contentsResp2.StatusCode != fiber.StatusOK {
t.Fatalf("expected 200 for cached contents, got %d", contentsResp2.StatusCode)
}
if contentsResp2.Header.Get("X-Any-Hub-Cache-Hit") != "true" {
t.Fatalf("expected cache hit for contents")
}
contentsResp2.Body.Close()
if stub.ReleaseGets() != 1 { if stub.ReleaseGets() != 1 {
t.Fatalf("expected single release GET, got %d", stub.ReleaseGets()) t.Fatalf("expected single release GET, got %d", stub.ReleaseGets())
} }
@@ -126,6 +145,12 @@ func TestAptUpdateCachesIndexes(t *testing.T) {
if stub.PackagesHeads() != 1 { if stub.PackagesHeads() != 1 {
t.Fatalf("expected single packages HEAD revalidate, got %d", stub.PackagesHeads()) t.Fatalf("expected single packages HEAD revalidate, got %d", stub.PackagesHeads())
} }
if stub.ContentsGets() != 1 {
t.Fatalf("expected single contents GET, got %d", stub.ContentsGets())
}
if stub.ContentsHeads() != 1 {
t.Fatalf("expected single contents HEAD revalidate, got %d", stub.ContentsHeads())
}
} }
type aptStub struct { type aptStub struct {
@@ -135,14 +160,19 @@ type aptStub struct {
mu sync.Mutex mu sync.Mutex
releaseBody string releaseBody string
packagesBody string packagesBody string
contentsBody string
releaseETag string releaseETag string
packagesETag string packagesETag string
contentsETag string
releaseGets int releaseGets int
releaseHeads int releaseHeads int
packagesGets int packagesGets int
packagesHeads int packagesHeads int
contentsGets int
contentsHeads int
releasePath string releasePath string
packagesPath string packagesPath string
contentsPath string
} }
func newAptStub(t *testing.T) *aptStub { func newAptStub(t *testing.T) *aptStub {
@@ -150,15 +180,19 @@ func newAptStub(t *testing.T) *aptStub {
stub := &aptStub{ stub := &aptStub{
releaseBody: "Release-body", releaseBody: "Release-body",
packagesBody: "Packages-body", packagesBody: "Packages-body",
contentsBody: "Contents-body",
releaseETag: "r1", releaseETag: "r1",
packagesETag: "p1", packagesETag: "p1",
contentsETag: "c1",
releasePath: "/dists/bookworm/Release", releasePath: "/dists/bookworm/Release",
packagesPath: "/dists/bookworm/main/binary-amd64/Packages.gz", packagesPath: "/dists/bookworm/main/binary-amd64/Packages.gz",
contentsPath: "/dists/bookworm/main/Contents-amd64.gz",
} }
mux := http.NewServeMux() mux := http.NewServeMux()
mux.HandleFunc(stub.releasePath, stub.handleRelease) mux.HandleFunc(stub.releasePath, stub.handleRelease)
mux.HandleFunc(stub.packagesPath, stub.handlePackages) mux.HandleFunc(stub.packagesPath, stub.handlePackages)
mux.HandleFunc(stub.contentsPath, stub.handleContents)
listener, err := net.Listen("tcp", "127.0.0.1:0") listener, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil { if err != nil {
@@ -212,6 +246,25 @@ func (s *aptStub) handlePackages(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte(s.packagesBody)) _, _ = w.Write([]byte(s.packagesBody))
} }
func (s *aptStub) handleContents(w http.ResponseWriter, r *http.Request) {
s.mu.Lock()
defer s.mu.Unlock()
if r.Method == http.MethodHead {
s.contentsHeads++
if matchETag(r, s.contentsETag) {
w.WriteHeader(http.StatusNotModified)
return
}
writeHeaders(w, s.contentsETag)
w.Header().Set("Content-Type", "application/gzip")
return
}
s.contentsGets++
writeHeaders(w, s.contentsETag)
w.Header().Set("Content-Type", "application/gzip")
_, _ = w.Write([]byte(s.contentsBody))
}
func matchETag(r *http.Request, etag string) bool { func matchETag(r *http.Request, etag string) bool {
for _, candidate := range r.Header.Values("If-None-Match") { for _, candidate := range r.Header.Values("If-None-Match") {
c := strings.Trim(candidate, "\"") c := strings.Trim(candidate, "\"")
@@ -251,6 +304,18 @@ func (s *aptStub) PackagesHeads() int {
return s.packagesHeads return s.packagesHeads
} }
func (s *aptStub) ContentsGets() int {
s.mu.Lock()
defer s.mu.Unlock()
return s.contentsGets
}
func (s *aptStub) ContentsHeads() int {
s.mu.Lock()
defer s.mu.Unlock()
return s.contentsHeads
}
func (s *aptStub) Close() { func (s *aptStub) Close() {
if s == nil { if s == nil {
return return