Evict remote objects from every cache layer
DELETE /objects only removed the artifacts row, so mutable indexes (S3 index object + Redis TTL/ETag keys) kept being served. Clear all layers and treat a trailing * as a prefix.
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
_ "git.unkin.net/unkin/artifactapi/internal/provider/rpm"
|
||||
"git.unkin.net/unkin/artifactapi/pkg/models"
|
||||
)
|
||||
|
||||
// changingUpstream serves every path with the current revision, as a mirror
|
||||
// does after a sync replaces its repodata.
|
||||
func changingUpstream(t *testing.T) (*httptest.Server, *atomic.Value) {
|
||||
t.Helper()
|
||||
var rev atomic.Value
|
||||
rev.Store("rev1")
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
v := rev.Load().(string)
|
||||
w.Header().Set("ETag", `"`+v+`"`)
|
||||
_, _ = w.Write([]byte(v + ":" + r.URL.Path))
|
||||
}))
|
||||
t.Cleanup(srv.Close)
|
||||
return srv, &rev
|
||||
}
|
||||
|
||||
func fetchBody(t *testing.T, r models.Remote, path string) string {
|
||||
t.Helper()
|
||||
res, err := testEngine.Fetch(context.Background(), r, path, prov(t, models.PackageRPM))
|
||||
if err != nil {
|
||||
t.Fatalf("fetch %s: %v", path, err)
|
||||
}
|
||||
return readAll(t, res)
|
||||
}
|
||||
|
||||
func rpmRemote(t *testing.T, name, baseURL string) models.Remote {
|
||||
return seed(t, models.Remote{Name: name, PackageType: models.PackageRPM, RepoType: models.RepoTypeRemote, BaseURL: baseURL, MutableTTL: 7200, CheckMutable: true})
|
||||
}
|
||||
|
||||
func TestEvictMutableIndexRefetches(t *testing.T) {
|
||||
requireStack(t)
|
||||
srv, rev := changingUpstream(t)
|
||||
r := rpmRemote(t, "evict-idx", srv.URL)
|
||||
const path = "8/Everything/x86_64/repodata/repomd.xml"
|
||||
|
||||
if got := fetchBody(t, r, path); got != "rev1:/"+path {
|
||||
t.Fatalf("initial fetch = %q", got)
|
||||
}
|
||||
rev.Store("rev2")
|
||||
if got := fetchBody(t, r, path); got != "rev1:/"+path {
|
||||
t.Fatalf("within TTL = %q, want cached rev1", got)
|
||||
}
|
||||
if err := testEngine.Evict(context.Background(), r.Name, path); err != nil {
|
||||
t.Fatalf("evict: %v", err)
|
||||
}
|
||||
if got := fetchBody(t, r, path); got != "rev2:/"+path {
|
||||
t.Fatalf("after evict = %q, want rev2", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvictWildcardClearsPrefixOnly(t *testing.T) {
|
||||
requireStack(t)
|
||||
srv, rev := changingUpstream(t)
|
||||
r := rpmRemote(t, "evict-wild", srv.URL)
|
||||
const (
|
||||
repomd = "8/Everything/x86_64/repodata/repomd.xml"
|
||||
rpm = "8/Everything/x86_64/Packages/a/a-1.0-1.el8.x86_64.rpm"
|
||||
other = "9/Everything/x86_64/repodata/repomd.xml"
|
||||
)
|
||||
for _, p := range []string{repomd, rpm, other} {
|
||||
fetchBody(t, r, p)
|
||||
}
|
||||
rev.Store("rev2")
|
||||
|
||||
if err := testEngine.Evict(context.Background(), r.Name, "8/Everything/x86_64/*"); err != nil {
|
||||
t.Fatalf("evict: %v", err)
|
||||
}
|
||||
if got := fetchBody(t, r, repomd); got != "rev2:/"+repomd {
|
||||
t.Errorf("index under prefix = %q, want rev2", got)
|
||||
}
|
||||
if got := fetchBody(t, r, rpm); got != "rev2:/"+rpm {
|
||||
t.Errorf("artifact under prefix = %q, want rev2", got)
|
||||
}
|
||||
if got := fetchBody(t, r, other); got != "rev1:/"+other {
|
||||
t.Errorf("path outside prefix = %q, want cached rev1", got)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user