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:
@@ -49,7 +49,7 @@ func TestLocalEvictCleansRPMMetadata(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
h := NewObjectsHandler(db)
|
||||
h := NewObjectsHandler(db, nil)
|
||||
router := chi.NewRouter()
|
||||
router.Route("/locals/{name}/objects", func(r chi.Router) {
|
||||
r.Delete("/*", h.LocalRoutes().ServeHTTP)
|
||||
|
||||
@@ -40,7 +40,7 @@ func TestLocalObjectsListing(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
h := NewObjectsHandler(db)
|
||||
h := NewObjectsHandler(db, nil)
|
||||
router := chi.NewRouter()
|
||||
router.Route("/locals/{name}/objects", func(r chi.Router) {
|
||||
r.Get("/", h.LocalRoutes().ServeHTTP)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
@@ -10,12 +11,18 @@ import (
|
||||
"git.unkin.net/unkin/artifactapi/internal/database"
|
||||
)
|
||||
|
||||
type ObjectsHandler struct {
|
||||
db *database.DB
|
||||
// Evictor drops a remote path from every cache layer.
|
||||
type Evictor interface {
|
||||
Evict(ctx context.Context, remoteName, path string) error
|
||||
}
|
||||
|
||||
func NewObjectsHandler(db *database.DB) *ObjectsHandler {
|
||||
return &ObjectsHandler{db: db}
|
||||
type ObjectsHandler struct {
|
||||
db *database.DB
|
||||
evictor Evictor
|
||||
}
|
||||
|
||||
func NewObjectsHandler(db *database.DB, evictor Evictor) *ObjectsHandler {
|
||||
return &ObjectsHandler{db: db, evictor: evictor}
|
||||
}
|
||||
|
||||
func (h *ObjectsHandler) Routes() chi.Router {
|
||||
@@ -86,7 +93,7 @@ func (h *ObjectsHandler) evict(w http.ResponseWriter, r *http.Request) {
|
||||
remoteName := chi.URLParam(r, "name")
|
||||
path := chi.URLParam(r, "*")
|
||||
|
||||
if err := h.db.DeleteArtifact(r.Context(), remoteName, path); err != nil {
|
||||
if err := h.evictor.Evict(r.Context(), remoteName, path); err != nil {
|
||||
http.Error(w, fmt.Sprintf("evict failed: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
type fakeEvictor struct{ remote, path string }
|
||||
|
||||
func (f *fakeEvictor) Evict(_ context.Context, remote, path string) error {
|
||||
f.remote, f.path = remote, path
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestRemoteEvictDelegatesToEvictor(t *testing.T) {
|
||||
for _, path := range []string{"8/Everything/x86_64/repodata/repomd.xml", "8/Everything/x86_64/repodata/*"} {
|
||||
ev := &fakeEvictor{}
|
||||
h := NewObjectsHandler(nil, ev)
|
||||
router := chi.NewRouter()
|
||||
router.Route("/remotes/{name}/objects", func(r chi.Router) {
|
||||
r.Delete("/*", h.Routes().ServeHTTP)
|
||||
})
|
||||
w := httptest.NewRecorder()
|
||||
router.ServeHTTP(w, httptest.NewRequest("DELETE", "/remotes/epel/objects/"+path, nil))
|
||||
if w.Code != 204 || ev.remote != "epel" || ev.path != path {
|
||||
t.Errorf("DELETE %s: code=%d evicted=%q/%q", path, w.Code, ev.remote, ev.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user