Files
mcr/internal/oci/delete.go
Kyle Isom d5580f01f2 Migrate module path from kyle/ to mc/ org
All import paths updated to git.wntrmute.dev/mc/. Bumps mcdsl to v1.2.0.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 02:05:59 -07:00

93 lines
2.9 KiB
Go

package oci
import (
"errors"
"fmt"
"net/http"
"git.wntrmute.dev/mc/mcr/internal/db"
"git.wntrmute.dev/mc/mcr/internal/policy"
)
// handleManifestDelete handles DELETE /v2/<name>/manifests/<digest>.
// Per OCI spec, deletion by tag is not supported — only by digest.
func (h *Handler) handleManifestDelete(w http.ResponseWriter, r *http.Request, repo, reference string) {
if !h.checkPolicy(w, r, policy.ActionDelete, repo) {
return
}
// Reference must be a digest, not a tag.
if !isDigest(reference) {
writeOCIError(w, "UNSUPPORTED", http.StatusMethodNotAllowed,
"manifest deletion by tag is not supported; use digest")
return
}
repoID, err := h.db.GetRepositoryByName(repo)
if err != nil {
if errors.Is(err, db.ErrRepoNotFound) {
writeOCIError(w, "NAME_UNKNOWN", http.StatusNotFound,
fmt.Sprintf("repository %q not found", repo))
return
}
h.log.Error("manifest delete: lookup repository", "error", err, "repo", repo)
writeOCIError(w, "UNKNOWN", http.StatusInternalServerError, "internal error")
return
}
if err := h.db.DeleteManifest(repoID, reference); err != nil {
if errors.Is(err, db.ErrManifestNotFound) {
writeOCIError(w, "MANIFEST_UNKNOWN", http.StatusNotFound,
fmt.Sprintf("manifest %q not found", reference))
return
}
h.log.Error("manifest delete: delete from database", "error", err, "repo", repo, "digest", reference)
writeOCIError(w, "UNKNOWN", http.StatusInternalServerError, "internal error")
return
}
h.audit(r, "manifest_deleted", repo, reference)
h.log.Info("manifest deleted", "repo", repo, "digest", reference)
w.WriteHeader(http.StatusAccepted)
}
// handleBlobDelete handles DELETE /v2/<name>/blobs/<digest>.
// Removes manifest_blobs associations for this repo only. Does not delete
// the blob row or file — that is GC's responsibility.
func (h *Handler) handleBlobDelete(w http.ResponseWriter, r *http.Request, repo, digest string) {
if !h.checkPolicy(w, r, policy.ActionDelete, repo) {
return
}
repoID, err := h.db.GetRepositoryByName(repo)
if err != nil {
if errors.Is(err, db.ErrRepoNotFound) {
writeOCIError(w, "NAME_UNKNOWN", http.StatusNotFound,
fmt.Sprintf("repository %q not found", repo))
return
}
h.log.Error("blob delete: lookup repository", "error", err, "repo", repo)
writeOCIError(w, "UNKNOWN", http.StatusInternalServerError, "internal error")
return
}
if err := h.db.DeleteBlobFromRepo(repoID, digest); err != nil {
if errors.Is(err, db.ErrBlobNotFound) {
writeOCIError(w, "BLOB_UNKNOWN", http.StatusNotFound,
fmt.Sprintf("blob %q not found in repository", digest))
return
}
h.log.Error("blob delete: delete from database", "error", err, "repo", repo, "digest", digest)
writeOCIError(w, "UNKNOWN", http.StatusInternalServerError, "internal error")
return
}
h.audit(r, "blob_deleted", repo, digest)
h.log.Info("blob deleted", "repo", repo, "digest", digest)
w.WriteHeader(http.StatusAccepted)
}